Compare commits

..

1 commit

Author SHA1 Message Date
8225cbd170 fix: Даунгрейд для cis (php 7.4) 2025-07-02 16:39:51 +03:00
97 changed files with 1471 additions and 3244 deletions

View file

@ -8,21 +8,12 @@ namespace ctiso;
*/
class Adapter
{
/** @var array|object */
protected $adaptee;
/**
* @param array|object $adaptee
*/
public function __construct ($adaptee)
{
$this->adaptee = $adaptee;
}
/**
* @param string $name
* @return mixed
*/
public function get($name)
{
if (is_array ($this->adaptee)) {

View file

@ -4,12 +4,6 @@
namespace ctiso;
class Arr {
/**
* @param array<string|int, mixed> $data
* @param string|int $key
* @param mixed $default
* @return mixed
*/
static function get($data, $key, $default = null) {
return $data[$key] ?? $default;
}

View file

@ -3,18 +3,21 @@
namespace ctiso;
/**
* Коллекция
* @implements \ArrayAccess<string,mixed>
*
*/
class Collection implements \ArrayAccess
{
/** @var array */
protected $data = [];
/**
* Holds collective request data
*
* @var array
*/
protected $data = array();
/**
* Преобразование массива в коллекцию
*
* @param array $data
* @return bool
*/
public function import(array $data)
{
@ -24,8 +27,6 @@ class Collection implements \ArrayAccess
/**
* Преобразование коллекции в массив
*
* @return array
*/
public function export()
{
@ -33,13 +34,14 @@ class Collection implements \ArrayAccess
}
/**
* Store "request data" in GPC order.
*
* @param string $key
* @param mixed $value
*
* @return void
*/
public function set(string $key, mixed $value)
public function set($key/*: string*/, $value/*: any*/)
{
$this->data[$key] = $value;
}
@ -48,7 +50,7 @@ class Collection implements \ArrayAccess
* Read stored "request data" by referencing a key.
*
* @param string $key
* @param mixed $default
*
* @return mixed
*/
public function get($key, $default = null)
@ -56,93 +58,23 @@ class Collection implements \ArrayAccess
return isset($this->data[$key]) && $this->data[$key] != '' ? $this->data[$key] : $default;
}
/**
* @param string $key
* @param int $default
* @return int
*/
public function getInt(string $key, int $default = 0): int
public function getInt($key, $default = 0)
{
$value = $this->get($key);
// Фильтруем как целое число
if (is_numeric($value)) {
// Приводим к int, но сначала проверим, что не float с дробной частью
$floatVal = (float)$value;
if (is_finite($floatVal) && floor($floatVal) === $floatVal) {
return (int)$floatVal;
}
}
return $default;
return (int)$this->get($key, $default);
}
/**
* @param string $key
* @param string $default
* @return string
*/
public function getString(string $key, string $default = ''): string
public function getString($key, $default = '')
{
$value = $this->get($key);
if (is_string($value)) {
return $value;
}
if (is_numeric($value)) {
return (string)$value;
}
return $default;
return (string)$this->get($key, $default);
}
/**
* Получает булево значение
* Поддерживает: 1, '1', 'true', 'on', 'yes' true
* Иначе false
*/
public function getBool(string $key, bool $default = false): bool
{
$value = $this->get($key);
if (is_bool($value)) {
return $value;
}
if (is_string($value)) {
$value = strtolower(trim($value));
return in_array($value, ['1', 'true', 'on', 'yes'], true);
}
if (is_numeric($value)) {
return (bool)$value;
}
return $default;
}
function getArray(string $key, array $default = []): array {
$result = $this->get($key);
if (is_array($result)) {
return $result;
}
return $default;
}
/**
* @param string $key
* @param int $default
* @return int
*/
public function getNat(string $key, int $default = 1): int
public function getNat($key, $default = 1)
{
$result = (int)$this->get($key, $default);
return (($result > 0) ? $result : $default);
}
public function clear(): void
public function clear()
{
$this->data = [];
}

View file

@ -2,31 +2,19 @@
namespace ctiso;
use ctiso\HttpRequest;
use ctiso\Arr;
use ctiso\HttpRequest,
ctiso\Arr;
class ComponentRequest {
/** @var int */
public $component_id;
/** @var string */
public $component_title;
/** @var HttpRequest */
public $r;
/**
* @param int $c
* @param HttpRequest $r
*/
function __construct($c, HttpRequest $r) {
$this->component_id = $c;
$this->r = $r;
}
/**
* @param string $key
* @param mixed $default
* @return mixed
*/
function get($key, $default = null) {
if ($key == 'active_page') {
return $this->r->get($key);
@ -42,9 +30,6 @@ class ComponentRequest {
return $default;
}
/**
* @return string
*/
function getAction() {
return $this->r->getAction();
}

View file

@ -3,37 +3,27 @@
namespace ctiso\Connection;
use ctiso\File;
class HttpRequest
class HttpRequest
{
const POST = "POST";
const GET = "GET";
/** @var array Параметры запроса */
private $param = [];
/** @var string Содержание */
public $data = null;
/** @var string Адресс */
public $url;
/** @var string Метод */
public $method;
/** @var int */
private $param = array(); // Параметры запроса
public $data = null; // Содержание
public $url; // Адресс
public $method; // Метод
public $port = 80;
/** @var string */
public $host = "";
/** @var ?string */
public $proxy_host = null;
/** @var ?int */
public $proxy_port = null;
/** @var string */
public $http_version = 'HTTP/1.1';
function __construct() {
$this->method = self::GET;
}
/**
* Возвращает заголовок соединения
* @return string
*/
public function getHeader()
{
@ -46,58 +36,47 @@ class HttpRequest
$result .= $this->data;
return $result;
}
/**
* Установка параметров запроса
* @param string $name
* @param string $value
* @parma string $name
* @parma string $value
*/
public function setParameter($name, $value): void
public function setParameter($name, $value)
{
$this->param[$name] = $value;
}
/**
* Метод запроса GET или POST
* @param string $method
*/
public function setMethod($method): void
public function setMethod($method)
{
$this->method = $method;
}
/**
* Установка URL
* @param string $url
*/
public function setUrl($url): void
public function setUrl($url)
{
$this->url = $url;
$host = parse_url($this->url, PHP_URL_HOST);
if (!$host) {
throw new \RuntimeException("Не удалось получить хост");
}
$this->host = $host;
$this->host = parse_url($this->url, PHP_URL_HOST);
}
public function getUrl(): string
public function getUrl()
{
return $this->url;
}
/**
* Содержание запроса
* @param string $data
*/
public function setContent($data): void
public function setContent($data)
{
$this->setParameter("Content-length", (string)strlen($data));
$this->setParameter ("Content-length", strlen($data));
$this->data = $data;
}
/**
* Посылает запрос и возвращает страницу
* @return string|null
*/
public function getPage()
{
@ -110,7 +89,7 @@ class HttpRequest
$header = $this->getHeader();
fwrite($socket, $header);
$result = '';
$result = null;
while (! feof($socket)) {
$result .= fgets($socket, 128);
}
@ -120,12 +99,6 @@ class HttpRequest
return null;
}
/**
* Получение JSON
* @param string $url
* @param array $data
* @return array
*/
static function getJSON($url, $data) {
$query = http_build_query($data);
$q = $url . '?' . $query;

View file

@ -7,39 +7,30 @@ namespace ctiso\Connection;
class HttpResponse
{
/** @var int */
private $offset;
/** @var array */
private $param = [];
/** @var int */
private $param = array ();
private $code;
/** @var string */
public $response;
/** @var string */
public $version;
/** @var string */
public $data;
/**
* @param string $response HTTP ответ
*/
public function __construct($response)
{
$this->offset = 0;
$this->response = $response;
$this->parseMessage();
}
}
/**
* Обработка HTTP ответа
*/
private function parseMessage(): void
private function parseMessage()
{
$http = explode(" ", $this->getLine());
$this->version = $http[0];
$this->code = (int)$http[1];
$this->code = $http[1];
$line = $this->getLine();
$line = $this->getLine();
while ($offset = strpos($line, ":")) {
$this->param[substr($line, 0, $offset)] = trim(substr($line, $offset + 1));
$line = $this->getLine();
@ -47,12 +38,12 @@ class HttpResponse
if (isset($this->param['Transfer-Encoding']) && $this->param['Transfer-Encoding'] == 'chunked') {
//$this->data = substr($this->response, $this->offset);
$index = (int)hexdec($this->getLine());
$index = hexdec($this->getLine());
$chunk = [];
while ($index > 0) {
$chunk [] = substr($this->response, $this->offset, $index);
$this->offset += $index;
$index = (int)hexdec($this->getLine());
$index = hexdec($this->getLine());
}
$this->data = implode("", $chunk);
@ -64,33 +55,32 @@ class HttpResponse
/**
* Обработка строки HTTP ответа
*/
private function getLine(): string
private function getLine()
{
$begin = $this->offset;
$offset = strpos($this->response, "\r\n", $this->offset);
$result = substr($this->response, $begin, $offset - $begin);
$result = substr($this->response, $begin, $offset - $begin);
$this->offset = $offset + 2;
return $result;
}
/**
* Значение параметра HTTP ответа
* @param string $name Имя параметра
*/
public function getParameter($name): string
public function getParameter($name)
{
return $this->param[$name];
}
public function getData(): string
public function getData()
{
return $this->data;
}
/**
* Состояние
* Состояние
*/
public function getCode(): int
public function getCode()
{
return $this->code;
}

View file

@ -1,69 +1,63 @@
<?php
namespace ctiso\Controller;
use Exception;
use ctiso\Path;
use ctiso\Url;
use ctiso\Model\Factory;
use ctiso\HttpRequest;
use ctiso\Settings;
use ctiso\Database;
use ctiso\View\Composite;
use ctiso\View\View;
use App\Controller\State;
use Exception,
ctiso\Path,
ctiso\Url,
ctiso\Model\Factory,
ctiso\HttpRequest,
ctiso\Settings,
ctiso\Database,
ctiso\View\Composite,
ctiso\View\View,
App\Controller\State;
/**
* Контроллер страниц
*/
class Action implements ActionInterface
class Action
{
const TEMPLATE_EXTENSION = ".html"; // Расширение для шаблонов
const ACTION_PREFIX = "action"; // Префикс для функций действий
// Параметры устанавливаются при создании контроллера
public string $name = ''; // Имя модуля
/** @var \ctiso\Controller\Front */
public $name = ''; // Имя модуля
public $front;
public string $modulePath = ''; // Путь к модулю
public string $moduleTitle = '';
public $modulePath = null; // Путь к модулю
public $moduleTitle = '';
public string $viewPathPrefix = ''; // Путь к шаблонам контроллера
public $viewPathPrefix = ''; // Путь к шаблонам контроллера
/** Соединение с базой данных */
/**
* Соединение с базой данных
*/
public Database $db;
// Фильтры
/** @var ?\ctiso\Filter\ActionAccess Обьект хранит параметры доступа */
public $access = null;
/** @var ?\ctiso\Filter\ActionLogger Обьект для ведения лога */
public $logger = null;
/** @var Factory Обьект для создания моделей */
private $factory = null;
public $access = null; // Обьект хранит параметры доступа
public $logger = null; // Обьект для ведения лога
/** @var ?Url Параметры для ссылки */
public $part = null;
private $factory = null; // Ссылка на обьект создания модели
private $helpers = array(); // Помошники для действий
public $part = null; // Параметры для ссылки
/** @var \ctiso\Registry Ссылка на настройки */
public $config;
/** @var \ctiso\Role\User Обьект пользователя */
public $user;
public $config/*: Registry*/; // Ссылка на настройки
public $user/*: User*/; // Обьект пользователя
// Для Widgets
public $view = null;
public $childNodes = array();
public $ctrlValues = array();
public $childViews = array();
function __construct() {
$this->part = new Url();
}
public function setUp(): void {
public function setUp() {
}
/**
* Загрузка файла настроек
* @param string $name
* @return array
*/
public function loadConfig($name) {
$basePath = $this->config->get('site', 'path');
@ -77,40 +71,25 @@ class Action implements ActionInterface
return $settings;
}
public function getConnection(): Database
public function getConnection()
{
return $this->db;
}
/**
* Путь к установке модуля
* @param string $name
* @return string
*/
public function installPath($name)
{
$basePath = $this->config->get('system', 'path');
return Path::join($basePath, "modules", $name);
}
/**
* Добавляет подсказки
* @param View $view
* @param string $name
*/
public function addSuggest(View $view, $name): void {
public function addSuggest(View $view, $name) {
$suggest = [];
$file = Path::join($this->modulePath, 'help', $name . '.suggest');
if (file_exists($file)) {
$view->suggestions = include($file);
}
}
/**
* Поиск иконки
* @param string $icon
* @param int $size
* @return string Путь к иконке
*/
function findIcon($icon, $size) {
$webPath = $this->config->get('site', 'web');
return Path::join($webPath, 'icons', $size . 'x' . $size, $icon . '.png');
@ -130,8 +109,7 @@ class Action implements ActionInterface
$webPath = $this->config->get('system', 'web');
$list = [
Path::join($this->modulePath, 'templates', $this->viewPathPrefix)
=> Path::join($webPath, "modules", $this->name, 'templates', $this->viewPathPrefix),
Path::join($this->modulePath, 'templates', $this->viewPathPrefix) => Path::join($webPath, "modules", $this->name, 'templates', $this->viewPathPrefix),
Path::join($basePath, "templates") => Path::join($webPath, "templates")
];
@ -186,7 +164,6 @@ class Action implements ActionInterface
* 1. Можно переопределить действия
* 2. Использовать наследование чтобы добавить к старому обработчику новое поведение
* @param HttpRequest $request запроса
* @return View|string
*/
public function preProcess(HttpRequest $request)
{
@ -202,47 +179,29 @@ class Action implements ActionInterface
return $view;
}
/**
* Выполнение действия
* @param HttpRequest $request
* @return View|string|false
*/
public function execute(HttpRequest $request)
{
$result = $this->preProcess($request);
return $result;
if (!empty($result)) {
$this->view = $result;
}
$text = $this->render();
return $text;
}
/**
* Перенаправление на другой контроллер
* @param string $action
* @param HttpRequest $args
* @return mixed
*/
public function forward($action, HttpRequest $args) {
$actionFn = [$this, $action];
if (!is_callable($actionFn)) {
return false;
}
$value = call_user_func($actionFn, $args);
$value = call_user_func([$this, $action], $args);
return $value;
}
/**
* Страница по умолчанию
* @param HttpRequest $request
* @return string|false
*/
public function actionIndex(HttpRequest $request) {
return "";
}
/**
* Добавление части ссылки
* @param string $key
* @param string $value
*/
public function addUrlPart($key, $value): void {
public function addUrlPart($key, $value) {
$this->part->addQueryParam($key, $value);
}
@ -255,9 +214,10 @@ class Action implements ActionInterface
*/
public function nUrl($actionName, array $param = [])
{
$access = $this->access;
$access/*: ActionAccess*/ = $this->access;
$url = new Url();
//print_r([$name, $param]);
if ($access == null || $access->checkAction($actionName)) {
$moduleName = explode("\\", strtolower(get_class($this)));
if (count($moduleName) > 2) {
@ -278,7 +238,7 @@ class Action implements ActionInterface
/**
* Генерация ссылки на действие контроллера
* Ajax определяется автоматически mode = ajax используется для смены layout
* @param string $name
* @param $name
* @param array $param
* @return Url|null
*
@ -290,13 +250,31 @@ class Action implements ActionInterface
return $this->nUrl($name, array_merge(['mode' => 'ajax'], $param));
}
/**
* Добавление помошника контроллера
*/
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([$helper, $action], $request, $this);
} else {
return $helper->actionIndex($request, $this); // Вместо return response ???
}
}
}
/**
* Загрузка файла класса
* @deprecated Веместо его нужно использовать автозагрузку
* @param string $path
* @param mixed $setup
* @param string $prefix
* @return mixed
*/
public function loadClass($path, $setup = null, $prefix = '')
{
@ -308,11 +286,6 @@ class Action implements ActionInterface
throw new Exception("NO CLASS $path");
}
/**
* Загрузка настроек
* @param string $path
* @return array
*/
public function loadSettings($path)
{
$result = new Settings($path);
@ -320,21 +293,69 @@ class Action implements ActionInterface
return $result->export();
}
public function setView($name)
{
$this->view = $this->getView($name);
}
/**
* Установка идентификатора страницы
* @return int
* Установка заголовка для отображения
*/
public function setTitle($title)
{
$this->view->setTitle($title);
}
/**
* Добавление widget к отображению
*/
public function addChild($section, $node)
{
$this->childNodes[$section] = $node;
}
public function setValue($name, $value)
{
$this->ctrlValues[$name] = $value;
}
/**
* Добавление дочернего отображения к текущему отображению
*/
public function addView($section, $node)
{
$this->childViews[$section] = $node;
}
/**
* Генерация содержания
* Путаница c execute и render
*/
public function render()
{
$view = $this->view;
if ($view instanceof View) {
$this->view->assignValues($this->ctrlValues);
$node/*: Composite*/ = null;
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(HttpRequest $request) {
$pageId = time();
$request->session()->set('page', $pageId);
return $pageId;
}
/**
* Проверка идентификатора страницы
* @param int $page Идентификатор страницы
* @return bool
*/
function checkPageId(HttpRequest $request, $page)
{
if ($request->get('__forced__')) {
@ -346,14 +367,16 @@ class Action implements ActionInterface
return $result;
}
/**
* @return State
*/
function _getActionPath() {
return new State('index');
}
function redirect(string $action): void {
// Тоже убрать в метод Controller_Model
function getActionPath(HttpRequest $request, $action = null) {
$this->_getActionPath()->getPath($this, ($action) ? $action : $request->getAction());
}
function redirect($action/*: string*/) {
header('location: ' . $action);
exit();
}

View file

@ -1,28 +0,0 @@
<?php
namespace ctiso\Controller;
use ctiso\Database;
use ctiso\HttpRequest;
interface ActionInterface {
/**
* Действие может вернуть Шаблон или строку
*
* @param HttpRequest $request
* @return \ctiso\View\View|string|false
*/
function execute(HttpRequest $request);
function getConnection(): Database;
/**
* @param string $name
* @param class-string<\ctiso\View\View> $class
* @return \ctiso\View\View
*/
function getView($name, $class);
/**
* @param string $key
* @param string $value
*/
function addUrlPart($key, $value): void;
}

View file

@ -1,90 +1,79 @@
<?php
namespace ctiso\Controller;
use ctiso\HttpRequest,
ctiso\ComponentRequest,
ctiso\Arr,
ctiso\Path,
ctiso\File,
ctiso\Form\Form,
ctiso\View\Composite,
ctiso\Database,
ctiso\Database\PDOStatement,
ctiso\Collection,
ctiso\Registry,
ctiso\Controller\SiteInterface,
PHPTAL,
PHPTAL_PreFilter_Normalize;
use ctiso\HttpRequest;
use ctiso\ComponentRequest;
use ctiso\Arr;
use ctiso\Path;
use ctiso\File;
use ctiso\Form\Form;
use ctiso\View\Composite;
use ctiso\Database;
use ctiso\Collection;
use ctiso\Registry;
use ctiso\Controller\SiteInterface;
use ctiso\Database\PDOStatement;
use PHPTAL;
use PHPTAL_PreFilter_Normalize;
use ctiso\View\JsonView;
class FakeTemplate {
public $_data = [];
public $_name = '';
function __construct($name) {
$this->_name = $name;
}
function __set($key, $value) {
$this->_data[$key] = $value;
}
function execute() {
return json_encode($this->_data);
}
}
/**
* Класс компонента
*/
class Component
{
/** @var string[] */
public $viewPath = [];
/** @var string[] */
public $webPath = [];
public $viewPath = array();
public $webPath = array();
/** @var ?string */
public $template = null;
public string $templatePath;
public $templatePath;
/** @var int */
public $component_id;
/** @var string */
public $component_title;
/** @var string */
public $COMPONENTS_WEB;
public Registry $config;
public Database $db;
public Collection $parameter;
/** @var string */
public $output = 'html';
/** @var string */
public $module;
/** @var string */
public $item_module;
/**
* @var SiteInterface $site
* @var \App\Controller\Site
*/
public $site;
function before(): void {
function before() {
}
/**
* @param string $match
* @return string
*/
static function replaceContent($match) {
return \ctiso\Tales::phptal_component(htmlspecialchars_decode($match[3]));
}
/**
* @param string $text
* @return string
*/
static function applyComponents($text) {
$callback = fn($x) => self::replaceContent($x);
return preg_replace_callback('/<(\w+)(\s+[a-zA-Z\-]+=\"[^\"]*\")*\s+tal:replace="structure\s+component:([^\"]*)"[^>]*>/u',
$callback, $text);
return preg_replace_callback('/<(\w+)(\s+[a-zA-Z\-]+=\"[^\"]*\")*\s+tal:replace="structure\s+component:([^\"]*)"[^>]*>/u', 'ctiso\\Controller\\Component::replaceContent', $text);
}
/**
* Выполняет запрос компонента и возвращает результат
* Результат может быть строкой или View для обычных компонентов, или массивом для использования в сервисах
*
* @param HttpRequest $request
* @param bool $has_id
* @return mixed
*/
function execute(HttpRequest $request, $has_id = true) {
$crequest = new ComponentRequest($this->component_id, $request);
@ -96,36 +85,25 @@ class Component
}
$this->before();
$actionMethod = [$this, $action];
if (is_callable($actionMethod)) {
return call_user_func($actionMethod, $crequest);
if (method_exists($this, $action)) {
return call_user_func([$this, $action], $crequest);
} else {
return $this->actionIndex($crequest);
}
return $this->actionIndex($crequest);
}
/**
* Получить имя шаблона
* @param Registry $_registry
* @return string
*/
public function getTemplateName($_registry) {
public function getTemplateName($_registry/*: \ctiso\Settings*/) {
return (isset($_COOKIE['with_template']) && preg_match('/^[\w\d-]{3,20}$/', $_COOKIE['with_template']))
? $_COOKIE['with_template'] : ($_registry ? $_registry->get('site', 'template') : 'modern');
}
/**
* Получить шаблон
* @param string $name
* @return PHPTAL|JsonView
*/
public function getView($name)
{
if ($this->output === 'json') {
return new JsonView($name);
return new FakeTemplate($name);
}
/** @var Registry $config */
/* @var Registry $config */
$config = $this->config;
$default = $config->get('site', 'template');
$template = ($this->template) ? $this->template : $this->getTemplateName($config);
@ -135,7 +113,7 @@ class Component
foreach ($this->viewPath as $index => $viewPath) {
// Загружать шаблон по умолчанию если не найден текущий
$dir = Path::join($this->viewPath[$index], 'templates', $template);
if (is_dir($dir)) {
if(is_dir($dir)) {
$tpl = new PHPTAL(Path::join($this->viewPath[$index], 'templates', $template, $name));
$tpl->setPhpCodeDestination(PHPTAL_PHP_CODE_DESTINATION);
$selected = $index;
@ -170,19 +148,10 @@ class Component
return $tpl;
}
/**
* Возвращает путь к шаблону по умолчанию
* @return string
*/
function _getDefaultPath() {
return $this->viewPath[count($this->viewPath) - 1];
}
/**
* Возвращает путь к шаблону
* @param string $name
* @return string
*/
public function getTemplatePath($name) {
$registry = $this->config;
// Брать настройки из куков если есть
@ -196,10 +165,6 @@ class Component
return Path::join($this->viewPath[count($this->viewPath) - 1], 'templates', 'modern', $name);
}
/**
* Возвращает путь к шаблонам
* @return string
*/
public function getTemplateWebPath()
{
return Path::join($this->webPath[count($this->webPath) - 1], 'templates', 'modern');
@ -207,26 +172,19 @@ class Component
/**
* Создает модель
*
* @template T
* @param class-string<T> $modelName
* @return T
* @param string $name
* @return mixed
*/
public function getModel($modelName)
public function getModel($name)
{
$modelName = "App\\Mapper\\" . $name;
$model = new $modelName();
$model->config = $this->config;
$model->db = $this->db;
return $model;
}
/**
* @param string $key
* @param string $val
* @param PDOStatement $res
* @return array{value: string, name: string}[]
*/
public function options(string $key, string $val, $res) {
public function options($key, $val, $res/*: PDOStatement*/) {
$result = [];
while($res->next()) {
$result[] = ['value' => $res->getString($key), 'name' => $res->getString($val)];
@ -234,12 +192,7 @@ class Component
return $result;
}
/**
* @param array $list
* @param bool $selected
* @return array{value: string, name: string, selected: bool}[]
*/
public function optionsPair(array $list, $selected = false) {
public function optionsPair($list, $selected = false) {
$result = [];
foreach ($list as $key => $value) {
$result [] = ['value' => $key, 'name' => $value, 'selected' => $key == $selected];
@ -247,13 +200,7 @@ class Component
return $result;
}
/**
* Найти файл по пути
* @param string[] $pathList
* @param string $name
* @return string|null
*/
function findFile(array $pathList, string $name) {
function findFile($pathList, $name) {
foreach($pathList as $item) {
$filename = Path::join($item, $name);
if (file_exists($filename)) {
@ -263,10 +210,6 @@ class Component
return null;
}
/**
* Получить информацию о параметрах
* @return array<mixed>
*/
function getInfo() {
$filename = Path::join($this->viewPath[count($this->viewPath) - 1], 'install.json');
if (file_exists($filename)) {
@ -280,10 +223,8 @@ class Component
/**
* Генерация интерфейса для выбора галлереи фотографии
* @param Composite $view
* @param ?\ctiso\Form\OptionsFactory $options
*/
public function setParameters(Composite $view, $options = null): void
public function setParameters(Composite $view, $options = null)
{
$form = new Form();
@ -295,26 +236,19 @@ class Component
$view->component_title = $settings['title'];
}
/**
* @param \ctiso\Form\OptionsFactory $options
* @return array
*/
public function getFields($options = null) {
$form = new Form();
$settings = $this->getInfo();
$form->addFieldList($settings['parameter'], $options);
$form->addFieldList($this->getInfo()['parameter'], $options);
return $form->field;
}
/**
* Обьеденить с ComponentFactory
* @param string $expression
* @param SiteInterface $site
* @return Component
*/
static function loadComponent(string $expression, $site)
static function loadComponent($expression, $site/*: SiteInterface*/)
{
$expression = htmlspecialchars_decode($expression);
$offset = strpos($expression, '?');
$url = parse_url($expression);
@ -327,21 +261,16 @@ class Component
parse_str($query, $arguments);
}
$name = $path;
$config = $site->getConfig();
// FIXME: Если имя для компонента не задано то возвращаем пустой компонент
// Нужно дополнительно проверить и файл или в autoloader просто не найдет файл копонента
if (!$name) {
return new Component();
}
$config = $site->config;
$filename = ucfirst($name);
$path = Path::join ($config->get('site', 'components'), $name, $filename . '.php');
$className = implode("\\", ['Components', ucfirst($name), $filename]);
$component = null;
$component/*: Component*/ = null;
if (file_exists($path)) {
/** @var Component $component */
// require_once ($path);
$component = new $className();
$component->viewPath = [$config->get('site', 'components') . '/' . $name . '/'];
@ -349,9 +278,8 @@ class Component
$component->COMPONENTS_WEB = $config->get('site', 'web') . '/components/';
} else {
/** @var Component $component */
$component = new $className();
$template = $component->getTemplateName($site->getConfig());
$template = $component->getTemplateName($site->config);
$component->viewPath = [
// Сначало ищем локально
@ -380,7 +308,7 @@ class Component
$db = $site->getDatabase();
$component->db = $db;
$component->config = $site->getConfig();
$component->config = $site->config;
$component->site = $site;
$stmt = $db->prepareStatement("SELECT * FROM component WHERE code = ?");
@ -416,29 +344,22 @@ class Component
$editor = $component->getEditUrl();
if ($editor) {
$site->addComponentConfig($editor);
$site->componentsConfig[] = $editor;
}
return $component;
}
/**
* @return ?array{name: string, url: string}
*/
function getEditUrl() {
return null;
}
/**
* @param ComponentRequest $request
* @return array
*/
function raw_query($request)
function raw_query($request/*: ComponentRequest*/)
{
$arr = $request->r->export('get');
$param = [];
$parameter = $this->parameter;
$parameter/*: Collection*/ = $this->parameter;
foreach($parameter->export() as $key => $value) {
$param[$key] = $value;
}
@ -456,12 +377,7 @@ class Component
}
/**
* @param ComponentRequest $request
* @param array $list
* @return string
*/
function query($request, $list)
function query($request/*: ComponentRequest*/, $list)
{
$arr = $request->r->export('get');
@ -473,28 +389,10 @@ class Component
return '?' . http_build_query($arr);
}
/**
* @param string $name
* @param string $path
* @param array $shim
*/
function addRequireJsPath($name, $path, $shim = null): void {
function addRequireJsPath($name, $path, $shim = null) {
$this->site->addRequireJsPath($name, $path, $shim);
}
/**
* @param ComponentRequest $request
* @return mixed
*/
function actionIndex($request) {
return "";
}
/**
* @param HttpRequest $request
* @return array
*/
function getDefaultPageEnvironment($request) {
return [];
function actionIndex($request/*: ComponentRequest*/) {
}
}

View file

@ -5,37 +5,25 @@
* @package system.controller
*/
namespace ctiso\Controller;
use ctiso\Controller\Action;
use ctiso\Registry;
use ctiso\Database;
use ctiso\Filter\ActionAccess;
use ctiso\Filter\ActionLogger;
use ctiso\Path;
use ctiso\UserMessageException;
use ctiso\HttpRequest;
use ctiso\Role\User;
use ctiso\Controller\Action,
ctiso\Registry,
ctiso\Database,
ctiso\Collection,
ctiso\Filter\ActionAccess,
ctiso\Filter\ActionLogger,
ctiso\Path,
ctiso\UserMessageException,
ctiso\HttpRequest,
ctiso\Role\User;
class Front extends Action
{
/**
* Параметр по которому выбирается модуль
* @var string
*/
protected $_param;
/**
* Значение параметра по умолчанию
* @var string
*/
protected $default;
protected $_param; // Параметр по которому выбирается модуль
protected $default; // Значение параметра по умолчанию
/** @var array<string, Action> */
protected $modules = [];
protected $modules = array();
/**
* @param string $default
*/
public function __construct(Database $db, Registry $config, User $user, $default) {
parent::__construct();
$this->config = $config;
@ -44,13 +32,7 @@ class Front extends Action
$this->default = $default;
}
/**
* Проверяет загружен ли модуль
* @param string $name Имя модуля
* @return bool
*/
public function isLoaded($name): bool
{
public function isLoaded($name) {
return isset($this->modules[$name]);
}
@ -81,7 +63,6 @@ class Front extends Action
$ucpart = ucfirst($second);
$moduleClass = "Modules\\$ucname\\$ucpart";
/** @var Action $module */
$module = new $moduleClass();
// Инициализация модуля
@ -111,7 +92,7 @@ class Front extends Action
public function execute(HttpRequest $request)
{
$name = $request->getString('module', $this->default);
$name = $request->get('module', $this->default);
try {
return $this->loadModule($name, $request);
} catch (UserMessageException $ex) { //Исключение с понятным пользователю сообщением

View file

@ -1,64 +1,38 @@
<?php
namespace ctiso\Controller;
use ctiso\Settings,
ctiso\Path,
ctiso\Database\JsonInstall;
use ctiso\Settings;
use ctiso\Path;
use ctiso\Database\JsonInstall;
use ctiso\Database\Manager;
class Installer
class Installer
{
/** @var Manager */
protected $db_manager;
/** @var callable */
protected $installPath;
/** @var Settings */
public $_registry;
public $_registry;
public function __construct(Settings $_registry)
{
$this->_registry = $_registry;
}
/**
* Устанавливает параметры
* @param Manager $db_manager
* @param callable $installPath
*/
public function setUp($db_manager, $installPath): void
public function setUp($db_manager, $installPath)
{
$this->db_manager = $db_manager;
$this->installPath = $installPath;
}
/**
* Получение пути к файлу install.json
* @param string $name
* @return string
*/
function getSetupFile($name)
{
$setup = Path::join(call_user_func($this->installPath, $name), "install.json");
return $setup;
return $setup;
}
/**
* Получение пути к файлу unisntall.json
* @param string $name
* @return string
*/
function getUninstallFile($name)
{
function getUninstallFile($name) {
return Path::join(call_user_func($this->installPath, $name), "sql", "uninstall.json");
}
/**
* Проверка версии обновления
* @param string $name
* @return bool
*/
// Проверка версии обновления
function isChanged($name) // Информация о модулях
{
$item = $this->_registry->get($name);
@ -72,14 +46,6 @@ class Installer
return true;
}
/**
* Устанавливает SQL
* @param array $sql
* @param string $version_new
* @param string $version_old
* @param string $name
* @return array
*/
function installSQL(array $sql, $version_new, $version_old, $name)
{
$result = [];
@ -94,28 +60,18 @@ class Installer
return $result;
}
/**
* @param string $name
* @return void
*/
function uninstall($name): void
{
function uninstall($name){
$uninstall = $this->getUninstallFile($name);
if (file_exists($uninstall)) {
$json_installer = new JsonInstall($this->db_manager);
$json_installer->install($uninstall, null);
$json_installer->install($uninstall,null);
}
$this->_registry->removeKey($name);
$this->_registry->write();
}
/**
* Устанавливает обновления если есть
* @param string $name
* @param bool $force
* @return array
*/
function doUpdates($name, $force = false)
// Устанавливает обновления если есть
function doUpdates($name, $force = false) // Установка модуля
{
$result = [];
$setup = $this->getSetupFile($name);
@ -130,7 +86,7 @@ class Installer
$version_new = $settings->get('version');
if ($item) {
$version_old = $item['version'];
$version_old = $item['version'];
} else {
$version_old = "0.0";
$registry->writeKey([$name], []);
@ -140,15 +96,15 @@ class Installer
if (is_array($sql)) {
$res = $this->installSQL($sql, $version_new, $version_old, $name);
if ($res) {
$result[] = $res;
$result[]=$res;
}
}
}
}
// Обновление версии меню
$registry->removeKey($name);
$registry->set($name, [
'version' => $version_new,
'version' => $version_new,
'time' => filemtime($setup)
]);
// $registry->writeKey([$name], $settings->export());
@ -158,13 +114,7 @@ class Installer
return $result;
}
/**
* Устанавливает базу данных
* @param string $dbinit_path
* @param string|null $dbfill_path
*/
function install($dbinit_path, $dbfill_path = null): void
{
function install($dbinit_path, $dbfill_path = null) {
$json_installer = new JsonInstall($this->db_manager);
$json_installer->install($dbinit_path, $dbfill_path);
}

View file

@ -4,25 +4,14 @@ namespace ctiso\Controller;
use ctiso\HttpRequest;
class Request {
/** @var HttpRequest */
public $r;
/** @var string */
public $id;
/**
* @param HttpRequest $request
* @param string $id
*/
function __construct($request, $id) {
function __construct($request/*: HttpRequest*/, $id) {
$this->r = $request;
$this->id = $id;
}
/**
* @param string $name
* @param mixed $def
* @return mixed
*/
function get($name, $def = null) {
$v = $this->r->get($name);
$id = $this->id;

View file

@ -4,70 +4,56 @@
* Класс сервиса = Упрощенный компонент
*/
namespace ctiso\Controller;
use ctiso\Path;
use ctiso\Model\BaseMapper;
use ctiso\File;
use ctiso\Registry;
use ctiso\Database\PDOStatement;
use ctiso\Database;
use ctiso\Path,
ctiso\Model\BaseMapper,
ctiso\File,
ctiso\Registry,
ctiso\Database\PDOStatement;
class Service
{
/** @var array */
public $viewPath = [];
/** @var array */
public $webPath = [];
/** @var Registry */
public $config;
/** @var string */
public $config/*: Registry*/;
public $template;
/** @var string */
public $templatePath;
/** @var string */
public $COMPONENTS_WEB;
/** @var Database */
public $db;
/**
* Возвращает путь к шаблонам
* @param string $name Имя шаблона
* @return string
*/
public function getTemplatePath($name)
{
return Path::join($this->viewPath[0], 'templates', 'modern', $name);
}
/**
* Возвращает путь к шаблонам
* @return string
*/
public function getTemplateWebPath()
{
return Path::join($this->webPath[0], strtolower(get_class($this)), 'templates', 'modern');
}
/**
* @param string $name Имя модели
*/
private function getModelPath($name)
{
return Path::join ($this->config->get('system', 'path'), "model", $name . ".php");
}
/**
* Создает модель
* @param class-string $modelName
* @param string $name
* @return BaseMapper
*/
public function getModel($modelName)
public function getModel($name)
{
/** @var BaseMapper */
$model = new $modelName();
require_once ($this->getModelPath ($name));
$modelName = $name . "Mapper";
$model = new $modelName ();
$model->db = $this->db;
return $model;
}
/**
* @param string $key
* @param string $val
* @param PDOStatement $res
* @return array
*/
public function options($key, $val, $res) {
public function options($key, $val, $res/*: PDOStatement*/) {
$result = [];
while($res->next()) {
$result[] = ['value' => $res->getInt($key), 'name' => $res->getString($val)];
@ -75,11 +61,6 @@ class Service
return $result;
}
/**
* @param array $list
* @param bool $selected
* @return array
*/
public function optionsPair($list, $selected = false) {
$result = [];
foreach ($list as $key => $value) {
@ -87,10 +68,7 @@ class Service
}
return $result;
}
/**
* @return array
*/
function getInfo() {
$filename = Path::join($this->viewPath[0], 'install.json');
if (file_exists($filename)) {

View file

@ -3,39 +3,10 @@
namespace ctiso\Controller;
interface SiteInterface {
/**
* FIXME: Возвращает ресурс (но его тип опрделяется в App)
* @return mixed
*/
function getResource();
/**
* @return \ctiso\Database
*/
function loadComponent($expression);
function getDatabase();
/**
* @return \ctiso\Registry
*/
function getConfig();
/**
* @return \ctiso\Settings
*/
function getTheme();
/**
* @param array $config
*/
function addComponentConfig($config): void;
function addRequireJsPath(string $name, string $path, ?array $shim = null): void;
function addStyleSheet(string $url): void;
/**
* @param string $expression
* @return ?Component
*/
function loadComponent(string $expression);
/**
* @return array{string, string, string}
*/
function findTemplate(string $name);
function replaceImg(string $src, int $width, int $height): string;
function updatePageTime(int $time): void;
function setComponentConfig($config);
function addRequireJsPath($name, $path, $schim = null);
}

View file

@ -1,10 +1,7 @@
<?php
namespace {
if (!function_exists('sqliteLower')) {
/**
* @param string $str
* @return string
*/
function sqliteLower($str)
{
return mb_strtolower($str, 'UTF-8');
@ -13,28 +10,18 @@ namespace {
}
namespace ctiso {
use PDO;
use ctiso\Database\Statement;
use ctiso\Database\PDOStatement;
use ctiso\Database\IdGenerator;
use PDO,
ctiso\Database\Statement,
ctiso\Database\PDOStatement,
ctiso\Database\IdGenerator;
/**
* Класс оболочка для PDO для замены Creole
* @phpstan-type DSN = array{phptype: string, hostspec: string, database: string, username: string, password: string}
*/
class Database extends PDO
{
/** @var DSN */
public $dsn;
/**
* Создает соединение с базой данных
* @param string $dsn - DSN
* @param string|null $username - имя пользователя
* @param string|null $password - пароль
*/
public function __construct($dsn, $username = null, $password = null)
{
parent::__construct($dsn, $username, $password);
@ -44,51 +31,36 @@ namespace ctiso {
}
/**
* prepare возвращает только PDOStatement т.к установлен PDO::ERRMODE_EXCEPTION
* @param array $options
* @param string $sql
* @return PDOStatement|false
*
*/
function prepare(string $sql, array $options = []): PDOStatement
function prepare($sql, $options = [])
{
/** @var PDOStatement */
$result = parent::prepare($sql, $options);
return $result;
}
function query($query, $fetchMode = PDO::FETCH_INTO, mixed $_arg1 = null, mixed $_arg2 = null): PDOStatement {
/** @var PDOStatement */
$result = parent::query($query, $fetchMode);
return $result;
}
/**
* Возвращает DSN
* @return DSN
*/
public function getDSN()
{
return $this->dsn;
}
/**
* Возвращает true, если база данных Postgres
* @return bool
*/
public function isPostgres()
{
return ($this->dsn["phptype"] == "pgsql");
}
/**
* Создает соединение с базой данных
* @param array $dsn - DSN
* @return Database|null
*/
static function getConnection(array $dsn)
{
/** @var ?Database */
$connection = null;
if ($dsn['phptype'] == 'pgsql' || $dsn['phptype'] == 'mysql') {
$port = (isset($dsn['port'])) ? "port={$dsn['port']};" : "";
$connection = new self("{$dsn['phptype']}:host={$dsn['hostspec']}; $port dbname={$dsn['database']}", $dsn['username'], $dsn['password']);
$connection/*: Database*/ = new self("{$dsn['phptype']}:host={$dsn['hostspec']}; $port dbname={$dsn['database']}", $dsn['username'], $dsn['password']);
if ($dsn['phptype'] == 'pgsql') {
$connection->query('SET client_encoding="UTF-8"');
}
@ -100,9 +72,9 @@ namespace ctiso {
$connection = new self("{$dsn['phptype']}:");
$connection->sqliteCreateFunction('LOWER', 'sqliteLower', 1);
} elseif ($dsn['phptype'] == 'sqlite') {
$connection = new self("{$dsn['phptype']}:{$dsn['database']}");
$connection/*: Database*/ = new self("{$dsn['phptype']}:{$dsn['database']}");
$connection->setAttribute(PDO::ATTR_TIMEOUT, 5);
$mode = defined('SQLITE_JOURNAL_MODE') ? \SQLITE_JOURNAL_MODE : 'WAL';
$mode = defined('SQLITE_JOURNAL_MODE') ? SQLITE_JOURNAL_MODE : 'WAL';
$connection->query("PRAGMA journal_mode=$mode");
$connection->sqliteCreateFunction('LOWER', 'sqliteLower', 1);
}
@ -111,11 +83,9 @@ namespace ctiso {
}
/**
* Выполняет запрос к базе данных
* @param string $query - запрос
* @param ?array<string, mixed> $values - значения
* @return PDOStatement|bool
*/
public function executeQuery($query, $values = null): PDOStatement
public function executeQuery($query, $values = null)
{
$stmt = $this->prepare($query);
@ -124,21 +94,14 @@ namespace ctiso {
return $stmt;
}
/**
* Создает подготовленный запрос
* @param string $query - запрос
* @return Statement
*/
public function prepareStatement($query)
{
return new Statement($query, $this);
}
// Для совместимости со старым представлением баз данных CIS
/**
* Извлекает из базы все элементы по запросу (Для совместимости со старым представлением баз данных CIS)
* @param string $query - запрос
* @param ?array<string, mixed> $values - значения
* @return array<array<string, mixed>>
* Извлекает из базы все элементы по запросу
*/
public function fetchAllArray($query, $values = null)
{
@ -150,9 +113,7 @@ namespace ctiso {
/**
* Извлекает из базы первый элемент по запросу
* @param string $query - запрос
* @param ?array<string, mixed> $values - значения
* @return array<string, mixed>|false
* @return array|false
*/
public function fetchOneArray($query, $values = null)
{
@ -162,11 +123,6 @@ namespace ctiso {
return $sth->fetch(PDO::FETCH_ASSOC);
}
/**
* Преобразует значения в подготовленные значения
* @param array $values - значения
* @return ?array<string, string>
*/
private function prepareValues($values)
{
if (!$values) {
@ -189,14 +145,8 @@ namespace ctiso {
}
return $prep;
}
/**
* Создает INSERT запрос
* @param string $table - таблица
* @param array $values - значения
* @param bool $return_id - возвращать id
* @param string $index - индекс
* @return int|null
*/
function insertQuery($table, array $values, $return_id = false, $index = null)
{
@ -218,22 +168,15 @@ namespace ctiso {
return $result[$index];
} else {
$result = $this->fetchOneArray("SELECT $index AS lastid FROM $table WHERE OID = last_insert_rowid()");
if ($result === false) {
throw new \RuntimeException("Ошибка получения идентификатора");
}
return $result['lastid'];
}
}
return null;
}
/**
* Создает UPDATE запрос
* @param string $table - таблица
* @param array $values - значения
* @param string $cond - условие
*/
function updateQuery($table, array $values, $cond): void
function updateQuery($table, array $values, $cond)
{
$prep = $this->prepareValues($values);
$sql = "UPDATE $table SET " . implode(
@ -247,10 +190,6 @@ namespace ctiso {
$stmt->execute($prep);
}
/**
* Создает генератор идентификаторов
* @return IdGenerator
*/
function getIdGenerator()
{
return new IdGenerator($this);
@ -264,17 +203,12 @@ namespace ctiso {
function getNextId($seq)
{
$result = $this->fetchOneArray("SELECT nextval('$seq')");
if ($result === false) {
throw new \RuntimeException("Ошибка получения следующего идентификатора");
}
return $result['nextval'];
}
/**
* Закрывает соединение с базой данных
*/
function close(): void
function close()
{
return null;
}
}
}

View file

@ -4,40 +4,26 @@ namespace ctiso\Database;
use ctiso\Database;
class IdGenerator {
/** @var Database */
private $db;
function __construct(Database $db) {
$this->db = $db;
}
/**
* @return bool
*/
function isBeforeInsert() {
return false;
}
return false;
}
/**
* @return bool
*/
function isAfterInsert() {
return true;
return true;
}
/**
* @param string $seq
* @return int
*/
function getId($seq) {
if ($this->db->isPostgres()) {
$result = $this->db->fetchOneArray("SELECT nextval('$seq') AS nextval");
} else {
$result = $this->db->fetchOneArray("SELECT last_insert_rowid() AS nextval");
}
if (!$result) {
throw new \Exception("nextval failed");
}
}
return (int)$result['nextval'];
}
}

View file

@ -5,21 +5,13 @@ namespace ctiso\Database;
use ctiso\Database\Manager;
class JsonInstall {
/** @var Manager */
public $db_manager;
/** @var array */
public $serialColumns;
public function __construct(Manager $db_manager) {
$this->db_manager = $db_manager;
}
/**
* Установить базу данных
* @param string $dbinit_path
* @param ?string $dbfill_path
* @return int
*/
function install($dbinit_path, $dbfill_path = null) {
$dbinit_file = file_get_contents($dbinit_path);
if (is_string($dbinit_file)) {
@ -38,14 +30,8 @@ class JsonInstall {
$this->fillDataBase($dbfill_path);
}
$this->makeConstraints($initActions);
return 1;
}
/**
* Получить список таблиц, которые не существуют в базе данных
* @param array $tables
* @return array
*/
function missingTables($tables) {
$actual_tables = $this->db_manager->getAllTableNames();
$missingTables = [];
@ -56,13 +42,8 @@ class JsonInstall {
return $missingTables;
}
/**
* Создать таблицы
* @param array $initActions
* @param string $dbinit_path
* @return void
*/
function initDataBase(array $initActions, $dbinit_path) {
//Создать таблицы
function initDataBase($initActions/*: array*/, $dbinit_path) {
$pg = $this->db_manager->db->isPostgres();
if (!$pg) {
$refs = [];
@ -92,7 +73,7 @@ class JsonInstall {
}
}
if ($action["type"] != "alterReference") {
$this->db_manager->executeAction($action, $dbinit_path);
$this->db_manager->ExecuteAction($action, $dbinit_path);
}
}
@ -114,11 +95,7 @@ class JsonInstall {
}
}
/**
* Заполнить данными
* @param string $dbfill_file_path
* @return void
*/
//Заполнить данными
function fillDataBase($dbfill_file_path) {
$dbfill_file = file_get_contents($dbfill_file_path);
if (is_string($dbfill_file)) {
@ -151,11 +128,7 @@ class JsonInstall {
}
}
/**
* Обновить ключи serial и создать ограничения
* @param array $initActions
* @return void
*/
//Обновить ключи serial и создать ограничения
function makeConstraints($initActions) {
$pg = $this->db_manager->db->isPostgres();
if ($pg) {

View file

@ -7,95 +7,18 @@ use ctiso\Tools\SQLStatementExtractor;
use ctiso\Path;
use Exception;
/**
* @phpstan-type DropAction array{
* type:"dropTable",
* table_name:string
* }
*
* @phpstan-type CreateAction array{
* type:"createTable",
* table_name:string,
* constraints?:array{fields: array<string>, type: string}|string,
* fields:array<mixed>,
* }
*
* @phpstan-type AddColumnAction array{
* type:"addColumn",
* table_name:string,
* column_name:string,
* field:ColumnProps
* }
*
* @phpstan-type AlterReferenceAction array{
* type:"alterReference",
* table:string,
* column:string,
* refTable:string,
* refColumn:string
* }
*
* @phpstan-type RenameColumnAction array{
* type:"renameColumn",
* table:string,
* old_name:string,
* new_name:string
* }
*
* @phpstan-type ExecuteFileAction array{
* type:"executeFile",
* source:string,
* pgsql:?string
* }
*
* @phpstan-type CreateViewAction array{
* type:"createView",
* view:string,
* select:string
* }
*
* @phpstan-type InsertAction array{
* type:"insert",
* table_name:string,
* values:array<mixed>
* }
*
* @phpstan-type Action DropAction
* | CreateAction
* | AddColumnAction
* | AlterReferenceAction
* | RenameColumnAction
* | ExecuteFileAction
* | CreateViewAction
* | InsertAction
*
* @phpstan-type ColumnProps array{
* name:string,
* type:string,
* not_null:bool,
* default:?string,
* references:?array{refTable:string,refColumn:string}
* }
*/
class Manager
{
/** @var Database */
public $db;
public $db/*: Database*/;
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* Выполняет действие
* @param Action $action
* @param string $db_file
* @throws Exception
*/
public function executeAction(array $action, $db_file = ""): void
public function executeAction($action/*: array*/, $db_file = "")
{
switch ($action["type"]) {
switch($action["type"]) {
case "dropTable":
$this->dropTableQuery($action["table_name"], true);
break;
@ -132,85 +55,59 @@ class Manager
break;
default:
throw new Exception("unknown action " . $action["type"] . PHP_EOL);
throw new Exception("unknown action ". $action["type"] . PHP_EOL);
}
}
/**
* Дропает и создаёт SQL VIEW
* @param string $viewName
* @param string $selectStatement
*/
public function recreateView($viewName, $selectStatement): void
//Дропает и создаёт SQL VIEW
public function recreateView($viewName, $selectStatement)
{
$this->db->query("DROP VIEW " . $viewName);
$this->db->query("CREATE VIEW " . $viewName . " AS " . $selectStatement);
$this->db->query("DROP VIEW ".$viewName);
$this->db->query("CREATE VIEW ".$viewName." AS ".$selectStatement);
}
/**
* Дропает таблицу
* @param string $table
* @param bool $cascade
*/
public function dropTableQuery($table, $cascade = false): void
public function dropTableQuery($table, $cascade=false)
{
$statement = "DROP TABLE IF EXISTS " . $table;
if ($this->db->isPostgres() && $cascade) {
$statement = "DROP TABLE IF EXISTS ".$table;
if ($this->db->isPostgres()&&$cascade) {
$statement .= " CASCADE";
}
$this->db->query($statement);
}
/**
* Добавляет ссылку на другую таблицу
* @param string $table
* @param string $column
* @param string $refTable
* @param string $refColumn
*/
public function alterReference($table, $column, $refTable, $refColumn): void
public function alterReference($table, $column, $refTable, $refColumn)
{
$this->db->query("ALTER TABLE " . $table . " ADD CONSTRAINT " . $table . "_" . $column . "fk" . " FOREIGN KEY (" . $column . ") REFERENCES " . $refTable . " (" . $refColumn . ") ON DELETE CASCADE ON UPDATE CASCADE");
$this->db->query("ALTER TABLE ".$table." ADD CONSTRAINT ".$table."_".$column."fk"." FOREIGN KEY (".$column.") REFERENCES ".$refTable." (".$refColumn.")");
}
/**
* Извлечение информации о полях таблицы
* @param string $table
* @return array{type:string,not_null:bool,constraint:?string}[]|null
*/
//Извлечение информации о полях таблицы
public function tableInfo($table)
{
$pg = $this->db->isPostgres();
if ($pg) {
throw new Exception("Not implemented for postgres");
} else {
$results = $this->db->fetchAllArray("PRAGMA table_info(" . $table . ");");
$results = $this->db->fetchAllArray("PRAGMA table_info(".$table.");");
if (empty($results)) {
return null;
}
$fields = [];
foreach ($results as $result) {
$fields[$result["name"]] = [
"type" => $result["type"],
"not_null" => boolval($result["notnull"]),
"constraint" => ((bool) $result["pk"]) ? "PRIMARY KEY" : null
"type"=> $result["type"],
"not_null"=> boolval($result["notnull"]),
"constraint"=> ((bool) $result["pk"]) ? "PRIMARY KEY" : null
];
}
return $fields;
}
}
/**
* Переименование столбца в таблице
* @param string $table
* @param string $old_name
* @param string $new_name
*/
public function renameColumn($table, $old_name, $new_name): void
public function renameColumn($table, $old_name, $new_name)
{
$pg = $this->db->isPostgres();
if ($pg) {
$this->db->query("ALTER TABLE " . $table . " RENAME COLUMN " . $old_name . " TO " . $new_name);
$this->db->query("ALTER TABLE ".$table." RENAME COLUMN ".$old_name." TO ".$new_name);
} else {
$tmp_table = "tmp_" . $table;
$this->dropTableQuery($tmp_table);
@ -220,9 +117,9 @@ class Manager
return;
}
$data = $this->dumpTable($table);
$data/*: array*/ = $this->dumpTable($table);
$this->db->query("ALTER TABLE " . $table . " RENAME TO " . $tmp_table . ";");
$this->db->query("ALTER TABLE ".$table." RENAME TO ".$tmp_table.";");
$table_info[$new_name] = $table_info[$old_name];
unset($table_info[$old_name]);
$this->createTableQuery($table, $table_info, null);
@ -237,62 +134,42 @@ class Manager
}
}
/**
* Обновление ключа serial после ручной вставки
* @param string $table
* @param string $column
*/
public function updateSerial($table, $column): void
//Обновление ключа serial после ручной вставки
public function updateSerial($table, $column)
{
$this->db->query("SELECT setval(pg_get_serial_sequence('" . $table . "', '" . $column . "'), coalesce(max(" . $column . "),0) + 1, false) FROM " . $table);
$this->db->query("SELECT setval(pg_get_serial_sequence('".$table."', '".$column."'), coalesce(max(".$column."),0) + 1, false) FROM ".$table);
}
/**
* Возвращает определение столбца
* @param string $name
* @param ColumnProps $data
* @param bool $pg
* @return string
*/
public function columnDefinition($name, $data, $pg)
{
$constraint = isset($data['constraint']) ? " " . $data['constraint'] : "";
$constraint = isset($data['constraint']) ? " ".$data['constraint'] : "";
$references = "";
if (isset($data['references'])) {
$references = " REFERENCES " . $data['references']['refTable'] . '(' . $data['references']['refColumn'] . ')';
$references = " REFERENCES " . $data['references']['refTable'] . '(' .$data['references']['refColumn'] . ')';
}
if (isset($data["not_null"]) && $data["not_null"]) {
$constraint .= " NOT NULL";
$constraint .=" NOT NULL";
}
$type = $data['type'];
if (!$pg) {
if (strtolower($type) == "serial") {
if (strtolower($type)=="serial") {
$type = "integer";
}
//if (strtolower($type)=="boolean")
// $type = "integer";
}
return $name . " " . $type . $references . $constraint;
return $name." ".$type.$references.$constraint;
}
/**
* Добавляет столбец в таблицу
* @param string $table_name
* @param string $column_name
* @param ColumnProps $field
*/
public function addColumn($table_name, $column_name, $field): void
public function addColumn($table_name, $column_name, $field)
{
$pg = $this->db->isPostgres();
$q = "ALTER TABLE " . $table_name . " ADD COLUMN " .
$this->columnDefinition($column_name, $field, $pg);
$q = "ALTER TABLE ".$table_name." ADD COLUMN ".
$this->columnDefinition($column_name, $field, $pg);
$this->db->query($q);
}
/**
* Возвращает определение ограничения
* @param array{fields: string[], type: string} $c
* @return string
*/
public function getConstraintDef(array $c)
public function getConstraintDef($c/*: array*/)
{
if ($c['type'] == 'unique') {
return "UNIQUE(" . implode(", ", $c['fields']) . ")";
@ -300,15 +177,8 @@ class Manager
return "";
}
/**
* Создает таблицу
* @example createTableQuery('users',['id'=>['type'=>'integer','constraint'=>'PRIMARY KEY']])
* @param string $table
* @param array $fields
* @param array{fields: array<string>, type: string}|string|null $constraints
*/
public function createTableQuery($table, $fields, $constraints): void
//CreateTableQuery('users',['id'=>['type'=>'integer','constraint'=>'PRIMARY KEY']])
public function createTableQuery($table, $fields, $constraints)
{
$pg = $this->db->isPostgres();
if ($constraints) {
@ -327,24 +197,19 @@ class Manager
$this->db->query($statement);
}
/**
* Возвращает дамп таблицы
* @param string $table_name
* @return array
*/
public function dumpTable($table_name)
{
$pg = $this->db->isPostgres();
$result = [];
$data = $this->db->fetchAllArray("SELECT * FROM " . $table_name . ";");
$data = $this->db->fetchAllArray("SELECT * FROM ".$table_name.";");
if (!$pg) {
$table_fields = $this->tableInfo($table_name);
foreach ($table_fields as $name => $value) {
$type = strtolower($value['type']);
if ($type == "boolean") {
foreach ($data as &$row) {
foreach ($data as &$row) {
if (isset($row[$name])) {
$row[$name] = boolval($row[$name]);
}
@ -354,18 +219,14 @@ class Manager
}
foreach ($data as $r) {
$result[] = [
"type" => "insert",
"table_name" => $table_name,
"values" => $r
"type" => "insert",
"table_name" => $table_name,
"values" => $r
];
}
return $result;
}
/**
* Возвращает все имена таблиц
* @return list<string>
*/
public function getAllTableNames()
{
$result = [];
@ -381,10 +242,6 @@ class Manager
return $result;
}
/**
* Возвращает дамп всех таблиц
* @return array
*/
public function dumpInserts()
{
$table_names = $this->getAllTableNames();

View file

@ -7,16 +7,10 @@ use ctiso\Database\StatementIterator,
PDO;
use TheSeer\Tokenizer\Exception;
/**
* @implements \IteratorAggregate<int, array>
*/
class PDOStatement extends \PDOStatement implements \IteratorAggregate
{
/** @var int */
protected $cursorPos = 0;
/** @var array<int, mixed> */
public $cache = [];
/** @var ?array */
public $cache = array();
public $fields;
function getIterator(): \Iterator {
@ -26,15 +20,11 @@ class PDOStatement extends \PDOStatement implements \IteratorAggregate
protected function __construct() {
}
function rewind(): void {
function rewind() {
$this->cursorPos = 0;
}
/**
* @param int $rownum
* @return bool
*/
public function seek($rownum): bool {
public function seek($rownum) {
if ($rownum < 0) {
return false;
}
@ -45,19 +35,17 @@ class PDOStatement extends \PDOStatement implements \IteratorAggregate
return true;
}
function valid(): bool {
function valid() {
return true;
}
/**
* @return bool
*/
public function first() {
if ($this->cursorPos !== 0) { $this->seek(0); }
if($this->cursorPos !== 0) { $this->seek(0); }
return $this->next();
}
function next(): bool{
function next() {
if ($this->getRecordCount() > $this->cursorPos) {
if (!isset($this->cache[$this->cursorPos])) {
$this->cache[$this->cursorPos] = $this->fetch(PDO::FETCH_ASSOC);
@ -72,86 +60,49 @@ class PDOStatement extends \PDOStatement implements \IteratorAggregate
}
}
function key(): int {
function key() {
return $this->cursorPos;
}
/**
* @return mixed
*/
function current() {
return $this->cache[$this->cursorPos];
}
/**
* @return array|null
*/
function getRow() {
return $this->fields;
}
/**
* @param string $name
* @return int
*/
function getInt($name): int {
function getInt($name) {
if (!$this->fields) {
throw new \Exception('no fields');
}
return (int)$this->fields[$name];
}
/**
* @param string $name
* @return string
*/
function getBlob($name) {
return $this->fields[$name];
}
/**
* @param string $name
* @return string
*/
function getString($name) {
return $this->fields[$name] ?? null;
}
/**
* @param string $name
* @return bool
*/
function getBoolean($name) {
return (bool)$this->fields[$name];
}
/**
* @param string $name
* @return mixed
*/
function get($name) {
return $this->fields[$name];
}
/**
* @param string $name
* @return array
*/
function getArray($name) {
return StringUtil::strToArray($this->fields[$name]);
}
/**
* @return int
*/
function getRecordCount() {
return count($this->cache);
}
/**
* @param array $args
* @return bool
*/
function execute($args = null): bool {
$result = parent::execute($args);
return $result;

View file

@ -4,80 +4,48 @@
* Класс оболочка для PDOStatement для замены Creole
*/
namespace ctiso\Database;
use PDO;
use ctiso\Database;
use PDO,
ctiso\Database;
class Statement
{
/** @var ?int */
protected $limit = null;
/** @var ?int */
protected $offset = null;
/** @var never */
protected $statement = null;
/** @var array{int|string, mixed, int}[] */
protected $binds = [];
/** @var Database */
protected $binds = array();
protected $conn;
/** @var string */
protected $query;
/**
* @param string $query
* @param Database $conn
*/
function __construct($query, $conn) {
function __construct($query, $conn/*: Database*/) {
$this->query = $query;
$this->conn = $conn;
}
/**
* @param int|string $n
* @param int $value
*/
function setInt($n, $value): void {
function setInt($n, $value) {
$this->binds [] = [$n, $value, PDO::PARAM_INT];
}
/**
* @param int|string $n
* @param string $value
*/
function setString($n, $value): void {
function setString($n, $value) {
$this->binds [] = [$n, $value, PDO::PARAM_STR];
}
/**
* @param int|string $n
* @param mixed $value
*/
function setBlob($n, $value): void {
function setBlob($n, $value) {
$this->binds [] = [$n, $value, PDO::PARAM_LOB];
}
/**
* @param int $limit
*/
function setLimit($limit): void {
function setLimit($limit) {
$this->limit = $limit;
}
/**
* @param int $offset
*/
function setOffset($offset): void {
function setOffset($offset) {
$this->offset = $offset;
}
/**
* @return PDOStatement
*/
function executeQuery() {
if ($this->limit) {
$this->query .= " LIMIT {$this->limit} OFFSET {$this->offset}";
}
$stmt = $this->conn->prepare($this->query);
$stmt/*: PDOStatement*/ = $this->conn->prepare($this->query);
foreach ($this->binds as $bind) {
list($n, $value, $type) = $bind;
$stmt->bindValue($n, $value, (int) $type);

View file

@ -3,22 +3,14 @@
namespace ctiso\Database;
use PDO;
/**
* @implements \Iterator<array>
*/
class StatementIterator implements \Iterator
{
/** @var PDOStatement */
private $result;
/** @var int */
private $pos = 0;
/** @var int */
private $row_count;
/**
* @param PDOStatement $rs
*/
public function __construct($rs) {
public function __construct($rs/*: PDOStatement*/) {
$this->result = $rs;
$this->row_count = $rs->getRecordCount();
}
@ -42,18 +34,15 @@ class StatementIterator implements \Iterator
return $this->result->cache[$this->pos];
}
function next(): void {
function next(): void{
$this->pos++;
}
/**
* @param int $index
*/
function seek($index): void {
function seek($index) {
$this->pos = $index;
}
function count(): int {
function count() {
return $this->row_count;
}
}

View file

@ -4,19 +4,15 @@ namespace ctiso\Excel;
class DateTime
{
/** @var int */
public $value;
/**
* @param int $value
*/
function __construct($value)
function __construct($value)
{
$this->value = (int)$value;
}
function getString(): string
function getString()
{
return date('Y-m-d\TH:i:s.u', $this->value);
}
}
}

View file

@ -8,18 +8,11 @@ use XMLWriter,
Exception;
class Document {
/** @var string */
static $ns = "urn:schemas-microsoft-com:office:spreadsheet";
/** @var list<Table|callable> */
private $table = [];
/** @var array<string, array> */
protected $styles = [];
private $table = array ();
protected $styles = array();
/**
* Добавление таблицы в документ
* @param Table|callable $table Таблица или функция, возвращающая таблицу
*/
function addTable($table): void {
function addTable($table) {
$this->table [] = $table;
}
@ -29,7 +22,7 @@ class Document {
* @param array $values array Параметры стиля
* @param string $type Тип стиля
*/
function setStyle ($name, array $values, $type = 'Interior'): void
function setStyle ($name, array $values, $type = 'Interior')
{
if(!isset($this->styles[$name])) {
$this->styles[$name] = [];
@ -40,8 +33,7 @@ class Document {
/**
* Генерация стилей
*/
private function createStyles (XMLWriter $doc): void
{
private function createStyles (XMLWriter $doc) {
$doc->startElement('Styles');
foreach ($this->styles as $name => $sn) {
$doc->startElement('Style');
@ -73,22 +65,18 @@ class Document {
/**
* Преобразует переводы строки в спец символы
* @param string $s
* @return string
*/
function clean ($s) {
assert(is_string($s));
return strtr($s, ["\n" => "&#10;"]);
}
/**
* Сохраняет таблицу в формате Office 2003 XML
* http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats
*
* @param string $filename
* @throws Exception
* @return void
*/
function save($filename): void
function save($filename)
{
$doc = new XMLWriter();
if (!$doc->openUri($filename)) {

View file

@ -4,20 +4,16 @@ namespace ctiso\Excel;
class Number
{
/** @var int */
public $value;
/**
* @param int|float $value
*/
function __construct($value)
function __construct($value)
{
$this->value = (int)($value);
}
function getString(): string
function getString()
{
return (string) $this->value;
}
return $this->value;
}
}

View file

@ -10,16 +10,10 @@ use XMLWriter,
class TableCell
{
/** @var string|false */
public $style = false;
/** @var string */
public $value;
/** @var bool */
public $merge = false;
/**
* @param string $value Значение клетки
*/
function __construct ($value)
{
$this->value = $value;
@ -31,29 +25,16 @@ class TableCell
*/
class TableRow
{
/** @var string|false */
public $style = false;
/** @var TableCell[] */
public $cells = [];
/** @var int|false */
public $cells = array();
public $height = false;
/**
* Устанавливает значение для клетки
* @param int $y Номер столбца
* @param string $value Значение клетки
*/
function setCell($y, $value): void
function setCell($y, $value)
{
$this->cells[$y] = new TableCell($value);
}
/**
* Устанавливает стиль для клетки
* @param int $y Номер столбца
* @param string $name Имя стиля
*/
function setCellStyle($y, $name): void
function setCellStyle($y, $name)
{
$this->cells[$y]->style = $name;
}
@ -64,16 +45,12 @@ class TableRow
*/
class Table
{
/** @var int */
static $index;
/** @var string */
private $name;
/** @var TableRow[] */
protected $rows = [];
private $style;
protected $rows = array();
/** @var int|false */
protected $_splitVertical = false;
/** @var int|false */
protected $_splitHorizontal = false;
function __construct()
@ -83,30 +60,26 @@ class Table
/**
* Записать значение в клетку с заданными координатами
* @param int $x Номер ряда
* @param int $y Номер столбца
* @param string $value Значение клетки
*/
function setCell(int $x, int $y, $value): void
function setCell($x, $y, $value)
{
assert($x > 0);
assert($y > 0);
assert(is_numeric($x) && $x > 0);
assert(is_numeric($y) && $y > 0);
if(! isset($this->rows[$x])) {
$this->rows[$x] = new TableRow();
}
$row = $this->rows[$x];
$row/*: TableRow*/ = $this->rows[$x];
$row->setCell($y, $value);
}
/**
* Заполняет ряд начиная с указанного столбца значениями из массива
*/
function setRow(int $row, int $index, array $data): void
function setRow($row, $index, array $data)
{
assert($index > 0);
assert($row > 0);
assert(is_numeric($index) && $index > 0);
assert(is_numeric($row) && $row > 0);
reset($data);
for ($i = $index; $i < $index + count($data); $i++) {
@ -117,40 +90,40 @@ class Table
/**
* Устанавливает высоту ряда
* @param int $row Номер ряда
* @param int $value Высота ряда
* @param $row integer Номер ряда
* @parma $value real Высота ряда
*/
function setRowHeight (int $row, $value): void
function setRowHeight ($row, $value)
{
assert($row > 0);
assert(is_numeric($row) && $row > 0);
$this->rows[$row]->height = $value;
}
/**
* Устанавливает стиль ряда
* @param int $row Номер ряда
* @param string $name Имя стиля
* @param $row integer Номер ряда
* @parma $name string Имя стиля
*/
function setRowStyle(int $row, $name): void
function setRowStyle ($row, $name)
{
assert($row > 0);
assert(is_numeric($row) && $row > 0);
$this->rows[$row]->style = $name;
}
/**
* Обьединяет клетки в строке
* @param int $x Номер ряда
* @param int $cell Номер столбца
* @param bool $merge Количество клеток для обьединения
* @param $row Номер ряда
* @param $cell Номер столбца
* @param $merge Количество клеток для обьединения
*/
function setCellMerge(int $x, int $cell, $merge): void
function setCellMerge($x, $cell, $merge)
{
assert($x > 0);
assert($cell > 0);
assert(is_numeric($x) && $x > 0);
assert(is_numeric($cell) && $cell > 0);
$row = $this->rows[$x];
$row/*: TableRow*/ = $this->rows[$x];
$row->cells[$cell]->merge = $merge;
}
@ -160,20 +133,18 @@ class Table
* @param int $y Номер столбца
* @param string $name Имя стиля
*/
function setCellStyle ($row, $y, $name): void
function setCellStyle ($row, $y, $name)
{
if (isset($this->rows[$row])) {
if (isset($this->rows[$row]))
$this->rows[$row]->setCellStyle($y, $name);
}
}
/**
* Добавляет строку к таблице
* @return int Номер добавленной строки
*/
function addRow(int $index = 1, array $data = [""])
function addRow($index = 1, array $data = [""])
{
assert($index > 0);
assert(is_numeric($index) && $index > 0);
$offset = $this->getRows() + 1;
$this->setRow($offset, $index, $data);
@ -187,9 +158,8 @@ class Table
*/
function getRows()
{
// Высчитываем максимальный индекс, массив может быть разрежен поэтому используем array_keys
$keys = array_keys($this->rows);
return $keys === [] ? 0 : max($keys);
$keys/*: array*/ = array_keys($this->rows);
return max($keys);
}
/**
@ -199,15 +169,15 @@ class Table
*/
function getRowCells(TableRow $row)
{
$keys = array_keys($row->cells);
return $keys === [] ? 0 :max($keys);
$keys/*: array*/ = array_keys($row->cells);
return max($keys);
}
/**
* Разделяет таблицу на две части по вертикали
* @param int $n Количество столбцов слева
*/
function splitVertical($n): void {
function splitVertical($n) {
$this->_splitVertical = $n;
}
@ -215,7 +185,7 @@ class Table
* Разделяет таблицу на две части по горизонтали
* @param int $n Количество столбцов сверху
*/
function splitHorizontal($n): void {
function splitHorizontal($n) {
$this->_splitHorizontal = $n;
}
@ -226,16 +196,9 @@ class Table
* @return int
*/
function getColumns() {
$columns = array_map($this->getRowCells(...), $this->rows);
return $columns === [] ? 0 : max($columns);
return max(array_map([$this, 'getRowCells'], $this->rows));
}
/**
* Кодирование строки
* @deprecated Можно заменить на значение
* @param string $s Строка
* @return string
*/
function encode($s)
{
return $s;
@ -243,13 +206,8 @@ class Table
/**
* Генерация клетки таблицы (Переработать)
* @param TableCell $ncell Клетка таблицы
* @param XMLWriter $doc XMLWriter
* @param int $j Индекс клетки
* @param mixed $value Значение клетки
* @param bool $setIndex Устанавливать индекс клетки в атрибут ss:Index
*/
function createCell (TableCell $ncell, XMLWriter $doc, $j, mixed $value, $setIndex): void {
function createCell (TableCell $ncell, XMLWriter $doc, $j, $value/*: any*/, $setIndex) {
$doc->startElement("Cell");
if ($ncell->style) {
@ -257,11 +215,11 @@ class Table
}
if ($ncell->merge) {
$doc->writeAttribute('ss:MergeAcross', (string)$ncell->merge);
$doc->writeAttribute('ss:MergeAcross', $ncell->merge);
}
if ($setIndex) {
$doc->writeAttribute('ss:Index', (string)$j);
$doc->writeAttribute('ss:Index', $j);
}
$doc->startElement("Data");
@ -277,7 +235,7 @@ class Table
} else {
$doc->writeAttribute('ss:Type', "Number");
}
$doc->writeCdata($value);
$doc->writeCdata($this->encode($value));
}
$doc->endElement();
$doc->endElement();
@ -286,7 +244,7 @@ class Table
/**
* Генерация таблицы
*/
public function createTable (XMLWriter $doc): void {
public function createTable (XMLWriter $doc) {
$doc->startElement('Worksheet');
$doc->writeAttribute('ss:Name', $this->name);
@ -306,10 +264,10 @@ class Table
}
if ($this->rows[$i]->height) {
$doc->writeAttribute('ss:Height', (string)$this->rows[$i]->height);
$doc->writeAttribute('ss:Height', $this->rows[$i]->height);
}
/** @var TableRow $nrow */
$nrow = $this->rows[$i];
$nrow/*: TableRow*/ = $this->rows[$i];
// Флаг индикатор подстановки номера столбца
$setIndex = false;
for ($j = 1; $j <= $columns; $j++) {
@ -334,23 +292,23 @@ class Table
$doc->endElement();
}
protected function splitPane (XMLWriter $doc): void {
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', (string) $this->_splitVertical);
$doc->writeElement('LeftColumnRightPane', (string) $this->_splitVertical);
$doc->writeElement('SplitVertical', $this->_splitVertical);
$doc->writeElement('LeftColumnRightPane', $this->_splitVertical);
}
if ($this->_splitHorizontal) {
$doc->writeElement('SplitHorizontal', (string) $this->_splitHorizontal);
$doc->writeElement('TopRowBottomPane', (string) $this->_splitHorizontal);
$doc->writeElement('SplitHorizontal', $this->_splitHorizontal);
$doc->writeElement('TopRowBottomPane', $this->_splitHorizontal);
}
if ($this->_splitHorizontal && $this->_splitVertical) {
$doc->writeElement('ActivePane', (string) 0);
$doc->writeElement('ActivePane', (string)0);
} else if($this->_splitHorizontal) {
$doc->writeElement('ActivePane', (string) 2);
$doc->writeElement('ActivePane', (string)2);
}
$doc->endElement();
}

View file

@ -4,11 +4,6 @@ namespace ctiso;
use Exception;
class File {
/**
* @param string $filename
* @return string
* @throws Exception
*/
static function getContents($filename) {
$buffer = @file_get_contents($filename);
if ($buffer !== false) {

View file

@ -1,27 +1,20 @@
<?php
/**
* Фильтр действий
* Фильтр действий
*/
namespace ctiso\Filter;
use ctiso\HttpRequest;
use ctiso\Role\User;
use ctiso\Filter\UserAccess,
ctiso\HttpRequest,
ctiso\Role\User;
class ActionAccess
{
/** @var array */
public $access = [];
/** @var FilterInterface */
public $access = array();
public $processor;
/** @var User */
public $user;
public $user/*: User*/;
/**
* @param FilterInterface $processor
* @param User $user
*/
function __construct($processor, $user) {
function __construct($processor/*: Filter*/, $user) {
$this->processor = $processor;
$this->user = $user;
}
@ -30,19 +23,12 @@ class ActionAccess
* Проверка доступных действий для пользователя
* !! Реализация класса проверки действий не должна быть внутри Контроллера!!!
* Информация о доступе может быть в файле, базе данных и т.д.
*
* @param string $action
* @return bool
*/
function checkAction($action) {
// Импликация !! http://ru.wikipedia.org/wiki/Импликация
return (!isset($this->access[$action]) || in_array($this->user->access, $this->access[$action]));
}
/**
* @param HttpRequest $request
* @return mixed
*/
function execute(HttpRequest $request) {
$action = $request->getAction();
if(! $this->checkAction($action)) {

View file

@ -5,25 +5,15 @@ use ctiso\Role\UserInterface,
ctiso\HttpRequest;
/* Переделать формат Логов на список json */
class ActionLogger implements FilterInterface
{
/** @var array */
public $before = [];
/** @var resource */
class ActionLogger
{
public $before = array();
public $file;
/** @var UserInterface */
public $user;
/** @var string */
public $action;
/** @var \ctiso\Controller\ActionInterface */
public $processor;
/**
* @param \ctiso\Controller\ActionInterface $processor
* @param string $logPath
* @param UserInterface $user
*/
function __construct($processor, $logPath, $user) {
function __construct($processor/*: Filter*/, $logPath, $user/*: UserInterface*/) {
$this->processor = $processor;
$this->user = $user;

View file

@ -5,20 +5,13 @@ namespace ctiso\Filter;
class Authorization {
const SESSION_BROWSER_SIGN_SECRET = '@w3dsju45Msk#';
const SESSION_BROWSER_SIGN_KEYNAME = 'session.app.browser.sign';
/** @var string */
public $group;
/**
* @param string $group
*/
function __construct($group) {
$this->group = $group;
}
/**
* @param string $group
*/
static function isLogged($group = 'access'): bool {
static function isLogged($group = 'access') {
// echo session_status();
if (session_status() == PHP_SESSION_NONE) {
session_start();
@ -36,11 +29,7 @@ class Authorization {
return false;
}
/**
* @param int $id
* @param string $group
*/
static function enter($id, $group = 'access'): void {
static function enter($id, $group = 'access') {
// $db->executeQuery("UPDATE visitor SET sid = '' WHERE id_visitor = " . $result->getInt('id_user'));
// session_register("access");
// session_register("time");
@ -51,7 +40,7 @@ class Authorization {
$_SESSION ["time"] = time();
}
static function getRawSign(): string
static function getRawSign()
{
$rawSign = self::SESSION_BROWSER_SIGN_SECRET;
$signParts = ['HTTP_USER_AGENT'];
@ -59,16 +48,17 @@ class Authorization {
foreach ($signParts as $signPart) {
$rawSign .= '::' . ($_SERVER[$signPart] ?? 'none');
}
return $rawSign;
}
static function getBrowserSign(): string
static function getBrowserSign()
{
return md5(self::getRawSign());
}
function logout(): void {
function logout() {
session_destroy();
}
}

View file

@ -5,44 +5,29 @@
*/
namespace ctiso\Filter;
use ctiso\Database;
use ctiso\HttpRequest;
use ctiso\Controller\ActionInterface;
use ctiso\Controller\Action,
ctiso\HttpRequest;
class Filter implements ActionInterface
class Filter
{
/** @var ActionInterface */
public $processor;
/**
* @param ActionInterface $processor
*/
public function __construct($processor)
public function __construct($processor/*: Action*/)
{
$this->processor = $processor;
}
public function execute(HttpRequest $request)
{
return $this->processor->execute($request);
}
/**
* @param string $name
* @param class-string<\ctiso\View\View> $class
* @return \ctiso\View\View
*/
public function getView($name, $class = \ctiso\View\Top::class)
public function getView($name, $class = 'ctiso\\View\\Top')
{
return $this->processor->getView($name, $class);
}
public function getConnection(): Database
public function getConnection()
{
return $this->processor->getConnection();
}
public function addUrlPart($key, $value): void {
$this->processor->addUrlPart($key, $value);
}
}

View file

@ -1,12 +0,0 @@
<?php
namespace ctiso\Filter;
use ctiso\HttpRequest;
interface FilterInterface {
/**
* @param HttpRequest $request
* @return mixed
*/
function execute(HttpRequest $request);
}

View file

@ -2,34 +2,31 @@
/**
* Фильтр для проверки авторизации
*
* action: login(password, login)
* action: logout()
*/
namespace ctiso\Filter;
use ctiso\Filter\Filter;
use ctiso\HttpRequest;
use ctiso\Settings;
use ctiso\Registry;
use ctiso\Database;
use ctiso\Role\User;
use ctiso\Collection;
use ctiso\Path;
use ctiso\Database\PDOStatement;
// В класс авторизации передавать обьект для управления пользователем
// Вынести в отдельный файл
namespace ctiso\Filter;
use ctiso\Filter\Filter,
ctiso\HttpRequest,
ctiso\Settings,
ctiso\Registry,
ctiso\Database,
ctiso\Role\User,
ctiso\Collection,
ctiso\Path;
class Login extends Filter
{
const SESSION_BROWSER_SIGN_SECRET = '@w3dsju45Msk#';
const SESSION_BROWSER_SIGN_KEYNAME = 'session.app.browser.sign';
const AUTH_MAX_ATTEMPT = 10;
const AUTH_LAST_ATTEMPT_TIMER = 600;
/** @var string */
public $mode = 'ajax';
/** @var PDOStatement */
public $user;
/** @var User */
public $role;
/** @var Registry */
public $config;
function __construct($processor, User $role, Registry $config) {
@ -41,7 +38,7 @@ class Login extends Filter
/**
* Проверка авторизации
* @param HttpRequest $request
* @return bool Авторизовани пользователь или нет
* @return Boolean Авторизовани пользователь или нет
*/
public function isLoggin(HttpRequest $request)
{
@ -50,8 +47,8 @@ class Login extends Filter
switch ($request->getAction()) {
// Авторизация по постоянному паролю
case 'login':
$login = $request->getString('login', '') ;
$password = $request->getString('password');
$login = $request->get('login');
$password = $request->get('password');
$result = $this->role->getUserByLogin($login); // Поиск по логину
if ($result) {
@ -63,9 +60,6 @@ class Login extends Filter
$db = Database::getConnection($dsn);
$user = $db->fetchOneArray("SELECT * FROM users WHERE login = :login", ['login' => $login]);
if ($user === false) {
return false;
}
$userPassword = $user['password'];
} /*else if (time() - $result->getInt('lastupdate') > 60*60*24*60) {
// Проверить давность пароля, 60 дней
@ -73,7 +67,7 @@ class Login extends Filter
$request->set('lastupdate', true);
return false;
}*/
// Проверка на количества попыток авторизации
// Проверка на количества попыток авторизации
$lastAttempt = $result;
if ($lastAttempt->get('trie_count') >= self::AUTH_MAX_ATTEMPT /*&& time() - $lastAttempt['trie_time'] < self::AUTH_LAST_ATTEMPT_TIMER*/) {
@ -81,7 +75,7 @@ class Login extends Filter
$request->set('timeout_error', true);
break;
} else {
$this->role->resetTries($request->getString('login'));
$this->role->resetTries($request->get('login'));
}
}
// Извлечнеие пользователя из родительской CMS, для проверки пароля
@ -106,7 +100,7 @@ class Login extends Filter
$result = $this->role->getUserByLogin($login); // Поиск по логину
if ($result) {
$temp = md5($result->getString('password') . $result->getString('login') . $result->getString('sid'));
if ($password == $temp) {
if ($password == $temp) {
$this->enter($result);
return true;
}
@ -131,15 +125,11 @@ class Login extends Filter
return false;
}
/**
* Вход в систему
* @param PDOStatement $result
*/
private function enter($result): void
private function enter($result)
{
$this->user = $result;
$random = rand(0, 1024 * 1024);
$this->role->setSID((string)$random, $result);
$this->role->setSID($random, $result);
$_SESSION["group"] = $result->getInt('access');
$_SESSION["access"] = $result->getInt('id_user'); // id_user
@ -148,13 +138,10 @@ class Login extends Filter
$_SESSION["time"] = time();
}
/**
* @return mixed
*/
public function execute(HttpRequest $request): mixed
public function execute(HttpRequest $request)
{
$logged = $this->isLoggin($request);
if ($request->getString('action') == 'user_access') {
if ($request->get('action') == 'user_access') {
if ($logged) {
$result = [];
$result['fullname'] = $this->user->getString('patronymic') . " " . $this->user->getString('firstname');
@ -166,7 +153,7 @@ class Login extends Filter
}
}
if ($request->getString('action') == 'relogin') {
if ($request->get('action') == 'relogin') {
if ($logged) {
return json_encode(['result' => 'ok', 'message' => "Авторизация успешна"]);
} else {
@ -177,7 +164,7 @@ class Login extends Filter
if (!$logged) {
// Параметры при неправильной авторизации
// Действия по умолчанию !! Возможно переход на форму регистрации
if ($request->getString('mode') == 'ajax') {
if ($request->get('mode') == 'ajax') {
if (!$this->requestIsWhite($request)) {
return json_encode(['result' => 'fail', 'message' =>"NOT_AUTHORIZED"]);
}
@ -187,11 +174,8 @@ class Login extends Filter
}
} else if (isset($_SERVER['HTTP_REFERER'])) {
$arr = [];
parse_str(parse_url($_SERVER['HTTP_REFERER'] ?? '', PHP_URL_QUERY) ?: '', $arr);
if (isset($arr['back_page'])
&& is_string($arr['back_page'])
&& $request->getString('mode') != 'ajax')
{
parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY) ?? '', $arr);
if (isset($arr['back_page']) && $request->get('mode') != 'ajax') {
$request->redirect($arr['back_page']);
}
}
@ -200,23 +184,19 @@ class Login extends Filter
return $text;
}
/**
/* ---------------------
* Проверка на попадание реквеста в белый список
*/
public function requestIsWhite(Collection $request): bool {
*/
public function requestIsWhite(Collection $request) {
$module = $request->get('module');
$action = $request->get('action');
$moduleDir = explode('\\',$module)[0];
// TODO: Параметр для белого списка перенести в install.json
$file = Path::join($this->config->get('system', 'path'), 'modules', $moduleDir, 'filters', 'white.json');
if (file_exists($file)) {
$text = file_get_contents($file);
if (!$text) {
return false;
}
$whiteList = json_decode($text, true);
if (is_array($whiteList) && in_array($action, $whiteList, true)) {
$whiteList = json_decode(file_get_contents($file), true);
if (in_array($action, $whiteList)) {
return true;
}
}

View file

@ -5,9 +5,8 @@ use ctiso\Form\Field;
class CheckBox extends Field
{
/** @var bool */
public $checked = false;
function setValue($value): void
function setValue($value)
{
$this->value = $value;
$this->checked = $value;

View file

@ -4,51 +4,28 @@
*/
namespace ctiso\Form;
use ctiso\Form\OptionsFactory;
class Field
{
/** @var bool */
public $hidden = false;
/** @var string */
public $name;
/** @var string */
public $label; // Метка поля
/** @var mixed */
public $value; // Значение поля
/** @var string */
public $type = ""; // Каждому типу элемента соответствует макрос TAL
/** @var ?string */
public $error_msg = null;
/** @var ?mixed */
public $default = null;
/** @var bool */
public $error = false;
/** @var bool */
public $error_msg = null;
public $default = null;
public $error = false;
public $require = false;
/** @var ?string */
public $hint = null;
/** @var ?int */
public $maxlength = null;
/** @var ?string */
public $fieldset = null;
// Блоки (Убрать в отдельный класс!!!)
/** @var array */
public $_title = [];
/** @var string */
public $_title = array();
public $description = "";
/** @var array */
public $alias = [];
/**
* @param array $input
* @param OptionsFactory|null $factory
* @phpstan-ignore-next-line
*/
public $alias = array();
/** @phpstan-ignore-next-line */
public function __construct ($input = [], $factory = null)
{
{
$this->default = null;
if (isset($input['validate'])) {
$this->require = strpos($input['validate'], 'require') !== false;
@ -64,15 +41,12 @@ class Field
}
}
/**
* @param mixed $value
*/
function setValue($value): void
function setValue($value/*: any*/)
{
$this->value = $value;
}
function getId(): string
function getId()
{
return $this->name . '_label';
}

View file

@ -5,40 +5,30 @@
*/
namespace ctiso\Form;
use ctiso\Form\Field;
use ctiso\Form\Select;
use ctiso\Form\Input;
use ctiso\Validator\Validator;
use ctiso\HttpRequest;
use ctiso\Form\Field,
ctiso\Form\Select,
ctiso\Form\Input,
ctiso\View\View,
ctiso\Validator\Validator,
ctiso\HttpRequest;
/**
* Форма для ввода
*/
class Form {
/** @var array<Field> */
public $field = []; //Поля формы
/** @var array */
public $fieldsets = []; //Группы полей (fieldset). Некоторые поля могут не принадлежать никаким группам
public $field = array(); //Поля формы
public $fieldsets = array(); //Группы полей (fieldset). Некоторые поля могут не принадлежать никаким группам
/** @var string */
public $action = "";
/** @var string */
public $method = 'post';
/** @var string */
public $header;
/** @var array */
protected $replace;
/** @var array */
protected $before;
/** @var array<string> */
public $_title = [];
/** @var array */
public $alias = [];
/** @var class-string<Field>[] */
private $constructor = [];
public $_title = array();
public $alias = array();
private $constructor = array();
/**
* Строим форму по ее структуре. Каждому типу соответствует определенный класс.
@ -49,6 +39,7 @@ class Form {
'input' => Input::class,
// input с проверкой на заполненность
'inputreq' => Input::class,
'date' => Date::class,
'datereq' => Date::class,
'datetime' => DateTime::class,
@ -77,7 +68,7 @@ class Form {
}
function getId(): string
function getId()
{
return '_form_edit';
}
@ -86,18 +77,18 @@ class Form {
* Добавление конструкторя для поля формы
* @param string $name Краткое название поля
* @param class-string<Field> $class
* @return void
*/
public function addFieldClass($name, $class): void
public function addFieldClass($name, $class)
{
$this->constructor [$name] = $class;
}
/**
* Добавляет одно поле ввода на форму
* @param array{ type: string, name: string, hint?: string } $init
* @param OptionsFactory|null $factory
* @return Field
*/
public function addField(array $init, $factory = null): Field
public function addField(array $init, $factory = null)
{
assert(isset($init['type']));
assert(isset($init['name']));
@ -119,7 +110,8 @@ class Form {
/**
* Добавление fieldset на форму
*/
public function addFieldSet(array $fieldset): void
public function addFieldSet(array $fieldset)
{
$this->fieldsets[$fieldset['name']] = $fieldset;
}
@ -127,7 +119,8 @@ class Form {
/**
* Добавление массива fieldset на форму
*/
public function addFieldSetList(array $list): void
public function addFieldSetList(array $list)
{
foreach ($list as $fieldset) {
$this->addFieldSet($fieldset);
@ -137,9 +130,8 @@ class Form {
/**
* Добавляет список полей для формы
* @param array $list
* @param OptionsFactory|null $factory
*/
public function addFieldList(array $list, $factory = null): void
public function addFieldList(array $list, $factory = null)
{
foreach ($list as $init) {
$this->addField($init, $factory);
@ -149,7 +141,7 @@ class Form {
/**
* Устанавливает ошибки после проверки
*/
function setError(Validator $validator): void
function setError(Validator $validator)
{
foreach ($validator->getErrorMsg() as $name => $error)
{
@ -158,12 +150,7 @@ class Form {
}
}
/**
* Устанавливает ошибку для поля
* @param string $name
* @param string $message
*/
function setFieldError($name, $message): void
function setFieldError($name, $message)
{
$this->field[$name]->error = true;
$this->field[$name]->error_msg = $message;
@ -172,7 +159,7 @@ class Form {
/**
* Устанавливает значения из масива
*/
function setValues(HttpRequest $request): void {
function setValues(HttpRequest $request) {
foreach ($this->field as $key => $_) {
$value = $request->getRawData($this->method, $key);
$this->field[$key]->setValue($value);
@ -184,28 +171,20 @@ class Form {
* @param object $data
* @param array $schema Связь между элементами формы и свойствами обьекта
*/
public function fill($data, array $schema): void
public function fill($data, array $schema)
{
foreach ($schema as $key => $conv) {
list($value, $type) = $conv;
$convertFn = [\ctiso\Primitive::class, 'from_' . $type];
if (!is_callable($convertFn)) {
throw new \Exception('Не найден метод преобразования ' . $type);
}
$this->field[$key]->setValue(call_user_func($convertFn, $data->$value));
$this->field [$key]->setValue(call_user_func([\ctiso\Primitive::class, 'from_' . $type], $data->$value));
}
}
/**
* @param string $name
* @param mixed $value
*/
public function set($name, $value): void
public function set($name, $value)
{
$this->field[$name]->setValue($value);
}
function execute(): self
function execute()
{
return $this;
}

View file

@ -4,6 +4,5 @@ namespace ctiso\Form;
use ctiso\Form\Input;
class Hidden extends Input {
/** @var bool */
public $hidden = true;
}

View file

@ -1,7 +0,0 @@
<?php
namespace ctiso\Form;
interface OptionsFactory {
function create(Select $field, array $options): void;
}

View file

@ -5,7 +5,7 @@ use ctiso\Form\Select;
class QuestionType extends Select
{
function setValue($value): void
function setValue($value)
{
// Установить selected у options
$this->value = $value;

View file

@ -3,18 +3,10 @@
namespace ctiso\Form;
use ctiso\Form\Field;
/**
* @phpstan-type Option = array{value: string, name: string, selected?: bool, class?: string|false}
*/
class Select extends Field
{
/** @var Option[] */
public $options = [];
public $options = array();
/**
* @param array{ options?: Option[], 'options.pair'?: array } $input
* @param OptionsFactory $factory
*/
public function __construct ($input, $factory) {
parent::__construct($input);
@ -32,11 +24,6 @@ class Select extends Field
}
}
/**
* @param string[] $list
* @param bool $selected
* @return Option[]
*/
public function optionsPair($list, $selected = false) {
$result = [];
foreach ($list as $key => $value) {

View file

@ -5,7 +5,7 @@ use ctiso\Form\Select;
class SelectMany extends Select
{
function setValue(mixed $value): void
function setValue($value)
{
// Установить selected у options
if (!is_array($value)) { $value = [$value]; }

View file

@ -8,7 +8,7 @@ use ctiso\Form\Select;
class SelectOne extends Select
{
function setValue($value): void
function setValue($value)
{
// Установить selected у options
$this->value = $value;

View file

@ -1,13 +1,13 @@
<?php
/**
* Текстовое поле
* Текстовое поле
*/
namespace ctiso\Form;
use ctiso\Form\Field;
class TextArea extends Field {
function setValue($value): void
function setValue($value)
{
$this->value = $value;
}

View file

@ -1,6 +1,6 @@
<?php
/**
/**
* http://www.alternateinterior.com/2006/09/a-viewstate-for-php.html
* Управление состоянием между страницами
*/
@ -8,26 +8,16 @@ namespace ctiso\Form;
class ViewState // extends Collection
{
/** @var array */
private $values = [];
private $values = array();
/**
* Устанавливает значение
* @param string $name
* @param mixed $value
*/
function set($name, $value): void
function set($name, $value)
{
$this->values[$name] = $value;
}
/**
* Возвращает значение
* @param string ...$args
* @return mixed
*/
function get(...$args)
function get($_rest)
{
$args = func_get_args();
$result = $this->values;
foreach ($args as $name) {
if (!isset($result[$name])) {
@ -38,28 +28,16 @@ class ViewState // extends Collection
return $result;
}
/**
* Сохраняет состояние
* @return string
*/
function saveState(): string
function saveState()
{
return base64_encode(serialize($this->values));
}
/**
* Восстанавливает состояние
* @param string $value
*/
function restoreState($value): void
function restoreState($value)
{
$this->values = unserialize(base64_decode($value));
}
/**
* Возвращает состояние
* @return array
*/
function export()
{
return $this->values;

View file

@ -1,55 +1,42 @@
<?php
namespace ctiso;
/**
* Функциональное программирование в PHP
* package functional
*/
/**
* Эмуляция каррированой функции
*/
namespace ctiso;
class right {
/** @var array<mixed> */
protected $params;
/** @var callable */
protected $fn;
/**
* @param array $params
*/
public function __construct($params) {
$this->fn = array_shift($params);
$this->params = $params;
}
/**
* Применение функции
* @param mixed ...$params
* @return mixed
*/
function apply(...$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 {
/** @var array<mixed> */
protected $params;
/** @var callable */
protected $fn;
/**
* @param array $params
*/
public function __construct($params) {
$this->fn = array_shift($params);
$this->params = $params;
}
/**
* Применение функции
* @param mixed ...$params
* @return mixed
*/
function apply(...$params) {
function apply() {
$params = func_get_args();
array_splice ($params, 0, 0, $this->params);
return call_user_func_array ($this->fn, $params);
}
@ -57,25 +44,16 @@ class left {
define('__', '_ARGUMENT_PLACE_');
class partial {
/** @var array<mixed> */
protected $params;
/** @var callable */
protected $fn;
/**
* @param array $params
*/
public function __construct($params) {
$this->fn = array_shift($params);
$this->params = $params;
}
/**
* Применение функции
* @param mixed ...$params
* @return mixed
*/
function apply(...$params) {
function apply() {
$params = func_get_args();
$result = [];
$count = count($this->params);
for($i = 0, $j = 0; $i < $count; $i++) {
@ -94,22 +72,13 @@ class partial {
* Композиция функций
*/
class compose {
/** @var array<callable> */
protected $fns;
/**
* @param array<callable> $list
*/
function __construct($list) {
$this->fns = array_reverse($list);
}
/**
* Применение функций
* @param mixed ...$params
* @return mixed
*/
function apply (...$params) {
function apply () {
$params = func_get_args ();
$result = call_user_func_array($this->fns[0], $params);
$count = count($this->fns);
for ($i = 1; $i < $count; $i++) {
@ -121,44 +90,39 @@ class compose {
class Functions {
/**
* Частичное применение функции
* @param mixed ...$args
* @return mixed
*/
static function partial(...$args) {
$closure = new partial($args);
static function partial($_rest) {
$closure = new partial(func_get_args());
return [$closure, 'apply'];
}
/**
* Композиция функций
* @param mixed ...$args
* @param array $_rest
* @return mixed
*/
static function compose(...$args) {
$closure = new compose($args);
static function compose($_rest) {
$closure = new compose(func_get_args());
return [$closure, 'apply'];
}
/**
* Карирование справа
* @param mixed ...$args
*
* @return mixed
*/
static function rcurry(...$args) {
$closure = new right($args);
static function rcurry($_rest) {
$closure = new right(func_get_args ());
return [$closure, 'apply'];
}
/**
* Карирование слева
* @param mixed ...$args
*
* @return mixed
*/
static function lcurry(...$args) {
$closure = new left($args);
static function lcurry($_rest) {
$closure = new left(func_get_args ());
return [$closure, 'apply'];
}
@ -183,8 +147,7 @@ class Functions {
}
/**
* @deprecated
* @param array<string, mixed> $value
* @param array $value
* @param string $name
*
* @return mixed
@ -193,21 +156,14 @@ class Functions {
return $value[$name];
}
/**
* @deprecated
* @param mixed $value
*
* @return mixed
*/
static function identity($value) {
return $value;
}
/**
* @deprecated use fn and <=> operator
* @param array $a
* @param array $b
* @param string|int $key
* @param $key
*
* @return int
*/
@ -218,14 +174,6 @@ class Functions {
return ($a[$key] > $b[$key]) ? -1 : 1;
}
/**
* @deprecated use fn and <=> operator
* @param array $a
* @param array $b
* @param string|int $key
*
* @return int
*/
static function __cmp_less($a, $b, $key) {
if ($a[$key] == $b[$key]) {
return 0;
@ -233,31 +181,50 @@ class Functions {
return ($a[$key] < $b[$key]) ? -1 : 1;
}
/**
* @param string ...$args
* @return string
*/
static function concat(...$args) {
// Сравнение по ключу массиве
static function __index($n, $key, $row) {
return ($row[$key] == $n);
}
static function __div($x, $y) {
return $x / $y;
}
static function __self($name, $o) {
return call_user_func([$o, $name]);
}
static function concat(/* $args ...*/) {
$args = func_get_args();
return implode("", $args);
}
/**
* @param mixed $x
* @return bool
*/
static function __empty($x) {
return empty($x);
}
// Отрицание
static function __not($x) {
return !$x;
}
// Не равно
static function __neq($x, $y) {
return $x != $y;
}
// Равно
static function __eq($x, $y) {
return $x == $y;
}
/**
* Извлекает из многомерого массива значения с определенным ключом
* @example key_values('a', array(1 => array('a' => 1, 'b' => 2))) => array(1)
*
* @param string $key
* @param list<array>|\ArrayIterator<int, array> $array
* @return mixed
*/
static function key_values($key, $array) {
static function key_values($key, array $array) {
$result = [];
foreach($array as $item) {
@ -266,12 +233,7 @@ class Functions {
return $result;
}
/**
* @param string $key
* @param array<object>|\ArrayIterator<int, object> $array
* @return array<mixed>
*/
static function key_values_object($key, $array) {
static function key_values_object($key, $array/*: array|ArrayIterator*/) {
$result = [];
foreach($array as $item) {
@ -280,12 +242,6 @@ class Functions {
return $result;
}
/**
* @param string $key
* @param string $value
* @param array<array<string, mixed>>|\ArrayIterator<int, array> $array
* @return array<string, mixed>
*/
static function assoc_key_values($key, $value, $array) {
$result = [];
foreach ($array as $item) {
@ -294,11 +250,6 @@ class Functions {
return $result;
}
/**
* @param string $key
* @param array<array<string, mixed>>|\ArrayIterator<int, array> $array
* @return array<string, mixed>
*/
static function assoc_key($key, $array) {
$result = [];
foreach ($array as $item) {
@ -307,34 +258,16 @@ class Functions {
return $result;
}
/**
* Возвращает значение по ключу
* @param string $key
* @param mixed $value
* @param array $array
* @return mixed
*/
static function _get($key, $value, $array) {
static function _get($key, $value/*: any*/, $array/*: array*/) {
foreach ($array as $item) {
if ($item[$key] == $value) {
return $item;
}
if ($item[$key] == $value) return $item;
}
return null;
}
/**
* Возвращает ключ по значению
* @param string $key
* @param mixed $value
* @param array $array
* @return mixed
*/
static function _get_key($key, $value, $array) {
foreach ($array as $name => $item) {
if ($item[$key] == $value) {
return $name;
}
if ($item[$key] == $value) return $name;
}
return null;
}
@ -342,8 +275,6 @@ class Functions {
/**
* Логическа операция && ко всем элементам массива
* @param array<mixed> $array Массив
* @param callable $callback Функция
* @return bool
*/
static function every(array $array, $callback) {
@ -357,11 +288,14 @@ class Functions {
/**
* Логическа операция || ко всем элементам массива
* @param array<mixed> $array Массив
* @param callable $callback Функция
* @param array $array
* @param mixed $callback
*
* @return mixed
*/
static function some(array $array, callable $callback) {
static function some(array $array, $callback) {
assert(is_callable($callback));
foreach ($array as $key => $value) {
if (call_user_func($callback, $value) === true) {
return $key;
@ -370,14 +304,9 @@ class Functions {
return false;
}
/**
* Разбивает массив на массивы определенной длины
* @template T
* @param int $length Длина массива
* @param T[] $array Массив
* @return T[][]
*/
static function span(int $length, array $array) {
static function span($length, array $array) {
assert(is_int($length));
$result = [];
$count = count($array);
for($i = 0; $i < $count; $i += $length) {
@ -386,60 +315,45 @@ class Functions {
return $result;
}
/**
* Возвращает значение массива
* @param array $data Массив
* @param string|int $n Ключ
* @return mixed
*/
static function array_ref(array $data, string|int $n) {
static function array_ref($data, $n) {
return $data[$n];
}
/**
* Вызывает функцию с аргументами
* @param mixed ...$args Аргументы
* @return mixed
*/
static function call(...$args) {
static function call() {
$args = func_get_args();
$name = array_shift($args);
return call_user_func_array($name, $args);
}
/**
* Поиск элемента в массиве
* @param callable $cb сравнение с элементом массива
* @param mixed $cb сравнение с элементом массива
* @param array $hs массив в котором ищется значение
* @param bool $strict
*
* @return int|string|null ключ найденого элемента в массиве
*/
static function array_usearch($cb, array $hs, $strict = false) {
foreach($hs as $key => $value) {
if (call_user_func_array($cb, [$value, $key, $strict])) {
return $key;
}
if (call_user_func_array($cb, [$value, $key, $strict])) return $key;
}
return null;
}
/**
* Выбирает все сроки из таблицы с уникальными значениями ключа
* @example
* key_unique_values ('name', array (array ('name' => 1), array ('name' => 2), array ('name' => 1)))
* => array (1, 2)
*
* @param string $name Имя ключа
* @param array $table Двухмерный массив
* @return array Массив с уникальными значениями ключа
* @example
* key_unique_values ('name', array (array ('name' => 1), array ('name' => 2), array ('name' => 1)))
=> array (1, 2)
* @end example
*/
static function key_unique_values ($name, $table) {
// Ищем уникальные значения для заданного ключа
$keys = [];
foreach ($table as $row) {
if (!in_array ($row[$name], $keys)) {
if (!in_array ($row[$name], $keys))
$keys[] = $row[$name];
}
}
return $keys;
}
@ -448,7 +362,6 @@ class Functions {
* Сортировка двумерного массива по заданному ключу
* @param array $array Массив
* @param string $key Имя ключа по значению которого будет идти сравнение
* @param callable $fn Функция сравнения
* @return array Отсортированный массив
*/
static function sortOn($array, $key, $fn = '\\ctiso\\Functions::__cmp') {
@ -459,11 +372,9 @@ class Functions {
/**
* Преобразует ключи элементов для многомерного массива
* @param string $key_name Имя ключа
* @param array $array Многомерный массив
* @return mixed
*/
static function hash_key ($key_name, $array) {
static function hash_key ($key_name,$array/*: array */) {
$result = [];
foreach($array as $value) {

View file

@ -4,19 +4,18 @@
* Неверный запрос
*/
namespace ctiso;
use ctiso\Collection;
use ctiso\Session;
use Exception,
ArrayAccess,
ctiso\Collection,
ctiso\Session;
/**
* @template T=mixed
* @template T
*/
class HttpRequest extends Collection
{
/** @var Session */
public $_session;
public $_session;
/**
* Constructor
* Stores "request data" in GPC order.
@ -42,51 +41,21 @@ class HttpRequest extends Collection
parent::set('files', $data);
}
/**
* @param string $key
* @return Collection
*/
function _get($key)
{
return parent::get($key);
}
/**
* @param string $key
* @param mixed $default
* @return null|string|array
* @param T $key
* @return mixed
*/
function get($key, $default = null)
{
return parent::get('data')->get($key, $default);
}
function getString(string $key, string $default = ''): string
{
return parent::get('data')->getString($key, $default);
}
function getInt(string $key, int $default = 0): int
{
return parent::get('data')->getInt($key, $default);
}
function getNat(string $key, int $default = 1): int
{
return parent::get('data')->getNat($key, $default);
}
function getBool(string $key, bool $default = false): bool
{
return parent::get('data')->getBool($key, $default);
}
function getArray(string $key, array $default = []): array
{
return parent::get('data')->getArray($key, $default);
}
function session(?Session $value = null): ?Session
function session(Session $value = null)
{
if ($value) {
$this->_session = $value;
@ -94,25 +63,19 @@ class HttpRequest extends Collection
return $this->_session;
}
function set(string $key, mixed $value): void
function set($key, $value/*: any*/)
{
parent::get('data')->set($key, $value);
}
/**
* @param string $key
* @return array<mixed>
*/
function export(string $key = 'data')
function export($key = 'data')
{
return parent::get($key)->export();
}
function clear(): void
function clear()
{
/** @var Collection */
$collection = parent::get('data');
$collection->clear();
return parent::get('data')->clear();
}
/**
@ -122,7 +85,7 @@ class HttpRequest extends Collection
* @param string $key
* @return mixed
*/
public function getRawData(string $var, $key)
public function getRawData($var, $key)
{
$data = parent::get(strtolower($var));
if ($data) {
@ -131,12 +94,6 @@ class HttpRequest extends Collection
return null;
}
/**
* @param string $var
* @param string $key
* @param mixed $val
* @return mixed
*/
public function setRawData($var, $key, $val)
{
$data = parent::get(strtolower($var));
@ -145,34 +102,28 @@ class HttpRequest extends Collection
}
}
/**
* @param string $name
*/
public function setAction($name): void
public function setAction($name)
{
$this->setRawData('get', 'action', $name);
}
public function getAction(): string
public function getAction()
{
$result = $this->getRawData('get', 'action');
return ($result) ? $result : 'index';
}
public function isAjax(): bool
public function isAjax()
{
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest');
}
/**
* @param string $url
*/
public function redirect($url): void {
public function redirect($url) {
header('location: ' . $url);
exit();
}
static function getProtocol(): string {
static function getProtocol() {
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
}
}

View file

@ -9,9 +9,6 @@ use ctiso\Filter\Filter,
class Blank extends Filter
{
/**
* @return mixed
*/
function execute(HttpRequest $request)
{
$text = $this->processor->execute($request);

View file

@ -5,44 +5,37 @@
* Выбор оформления страницы осуществляется если было совпадение с каким либо условием
*/
namespace ctiso\Layout;
use ctiso\Filter\Filter;
use ctiso\HttpRequest;
use ctiso\Filter\Filter,
ctiso\Functions,
ctiso\HttpRequest;
class Manager extends Filter
{
/**
* Массив условий с их макетами
* @var list<array{callable, Filter}>
*/
protected $condition = [];
// Массив условий с их макетами
protected $condition = array();
/**
* Функция которая добавляет условие для проверки параметров $_GET
* @param array|true $get Ассоциативный массив ключей и значений для $_GET
* @param Filter $layout Макет
* @param $get array() | true Ассоциативный массив ключей и значений для $_GET
*
* @example
* addConditionGet(array('module' => 'personal'), 'personal')
* addConditionGet(array('module' => 'login'), 'login')
*/
public function addConditionGet($get, Filter $layout): void
public function addConditionGet($get, Filter $layout)
{
$this->addCondition(fn(HttpRequest $request) => $this->checkGet($request, $get), $layout);
$this->addCondition(Functions::rcurry([$this, 'checkGet'], $get), $layout);
}
/**
* Условие для аякс запросов. Тоже самое что и addConditionGet но еще проверяется является ли запрос ajax
* @param array|true $get Ассоциативный массив ключей и значений для $_GET
* @param Filter $layout Макет
* Условие для аякс запросов. Тоже самое что и addConditionGet но еще проверяется является ли запрос ajax
*/
public function addConditionXHR($get, Filter $layout): void
public function addConditionXHR($get, Filter $layout)
{
$this->addCondition(fn(HttpRequest $request) => $this->checkXHR($request, $get), $layout);
$this->addCondition(Functions::rcurry([$this, 'checkXHR'], $get), $layout);
}
/**
* @param HttpRequest $request
* @param array|true $get
* @return bool
*/
public function checkGet($request, $get)
public function checkGet($request/*: HttpRequest*/, $get)
{
if (is_array($get)) {
foreach ($get as $key => $value) {
@ -54,44 +47,37 @@ class Manager extends Filter
return true;
}
/**
* @param HttpRequest $request
* @param array|true $get
* @return bool
*/
public function checkXHR($request, $get): bool
public function checkXHR($request/*: HttpRequest*/, $get)
{
return $request->isAjax() && $this->checkGet($request, $get);
}
/**
* Добавляет условие в общем виде
* @param callable $get Функция
* @param Filter $layout Макет
* @parma $get function(HttpRequest) Функция
* @parma $layout Layout Макет
*/
public function addCondition($get, Filter $layout): void
{
public function addCondition($get, Filter $layout)
{
$this->condition [] = [$get, $layout];
}
/**
* Выбирает и применяет макет для страницы
* @return mixed
*/
public function execute(HttpRequest $request): mixed
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 (is_object($view)) {
return $view->execute();
return $view->render();
} else {
return $view;
}
}
}
return '';
}
}

View file

@ -4,39 +4,26 @@
* Класс для работы с почтой
* http://en.wikipedia.org/wiki/MIME
*/
namespace ctiso;
use ctiso\Path,
Exception;
class Mail
{
/** @var string */
public $_from;
/** @var string */
public $_to;
/** @var string */
public $_subject;
/** @var string */
public $content;
/** @var string */
public $copy;
/** @var string */
private $encoding;
/** @var string */
private $encoding;
private $_notify = null;
/** @var array */
protected $attachment = array();
/** @var string */
protected $attachment = array ();
protected $uniqid;
/** @var string */
protected $type = "text/plain";
function __construct()
{
function __construct() {
$this->setEncoding("UTF-8");
$this->uniqid = strtoupper(uniqid((string)time())); // Идентефикатор разделителя
}
@ -44,35 +31,32 @@ class Mail
/**
* Установка отправителя
*/
function from(string $name): void
function from($name)
{
// filter_var($name, FILTER_VALIDATE_EMAIL);
$this->_from = $name;
}
}
/**
* Установка получателя
*/
function to(string $name): void
function to($name) // recipient
{
$this->_to = $name;
}
/**
* @param string $name
*/
function replyTo($name): void
{}
function replyTo($name) // recipient
{
}
/**
* Установка получателей копии
*/
function copy(string $name): void
function copy($name) // recipient cc
{
$this->copy = $name;
}
function notify(string $notify): void
function notify($notify)
{
$this->_notify = $notify;
}
@ -80,70 +64,59 @@ class Mail
/**
* Тема письма
*/
function subject(string $subject): void
function subject($subject)
{
$this->_subject = $subject;
$this->_subject = $subject;
}
/**
* Текст письма
*/
function setContent(string $text): void
function setContent($text)
{
$this->content = $text;
}
/**
* Кодировка текста в письме
*/
function setEncoding(string $encoding): void
function setEncoding($encoding)
{
$this->encoding = $encoding;
}
/**
* Добавление вложения из файла
* @param string $filename
* @param string|false $name
*/
function addAttachment(string $filename, $name = false): void
function addAttachment($filename, $name = false)
{
if (file_exists($filename)) {
assert(is_string($filename));
if(file_exists($filename)) { // assert ??
$file = fopen($filename, "rb");
if (is_resource($file)) {
$size = filesize($filename);
if ($size !== false && $size > 0) {
$data = fread($file, $size);
$this->attachment[] = ($name) ? [$data, $name] : [$data, basename($filename)];
}
$data = fread($file, filesize($filename));
$this->attachment [] = ($name) ? [$data, $name] : [$data, basename($filename)];
}
}
}
/**
* Установка типа содержимого
* @param string $type
*/
function setType($type): void
function setType($type)
{
$this->type = $type;
}
/**
* Добавление вложения из строки с указанием имени файла
* @param string $data
* @param string $name
*/
function addAttachmentRaw($data, string $name): void
function addAttachmentRaw($data, $name)
{
$this->attachment[] = [$data, $name];
assert(is_string($name));
$this->attachment [] = [$data, $name];
}
/**
* @param string $var
* @param string $val
*/
function quote($var, $val): string
function quote($var, $val)
{
return ";" . PHP_EOL . "\t" . $var . "=\"" . $val . "\"";
}
@ -151,40 +124,36 @@ class Mail
/**
* Общий формат тегов MIME
* @see http://tools.ietf.org/html/rfc2045
*
* @param string $name
* @param string $value
* @param array $args
*/
function mimeTag($name, $value, array $args = []): string
function mimeTag($name, $value, array $args = [])
{
assert(is_string($name));
assert(is_string($value));
assert (is_string($name));
assert (is_string($value));
return $name . ": " . $value . implode("", array_map([$this, 'quote'], array_keys($args), array_values($args))) . PHP_EOL;
return $name . ": " . $value . implode("", array_map([$this, 'quote'], array_keys($args), array_values($args))) . PHP_EOL;
}
/**
*
* @see http://tools.ietf.org/html/rfc2047
*
* @see http://tools.ietf.org/html/rfc2047
*/
function encodedWord(string $text, string $encoding = 'B'): string
function encodedWord($text, $encoding = 'B')
{
return "=?{$this->encoding}?$encoding?" . base64_encode($text) . "?=";
return "=?{$this->encoding}?$encoding?".base64_encode($text)."?=";
}
/**
* Тело сообщения
*/
function getMessage(): string
function getMessage()
{
$message = "--" . $this->uniqid . PHP_EOL;
$message = "--".$this->uniqid . PHP_EOL;
$message .= $this->mimeTag("Content-Type", $this->type, ['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) {
@ -202,7 +171,7 @@ class Mail
/**
* Заголовок сообщения
*/
function getHeader(): string
function getHeader()
{
$head = $this->mimeTag("MIME-Version", "1.0");
$head .= $this->mimeTag("From", $this->_from);
@ -219,7 +188,7 @@ class Mail
return $head;
}
function getSubject(): string
function getSubject()
{
return $this->encodedWord($this->_subject);
}
@ -227,28 +196,27 @@ class Mail
/**
* Вывод строки для сохранения в формате .eml
*/
function eml(): string
function eml()
{
return "To: " . $this->_to . PHP_EOL . "Subject: {$this->getSubject()}" . PHP_EOL . $this->getHeader() . $this->getMessage();
}
/**
* Отправка почты
* Отправка почты
*/
function send(): bool
function send()
{
$result = mail($this->_to, $this->getSubject(), $this->getMessage(), $this->getHeader());
if (! $result) {
if(! $result) {
file_put_contents(Path::resolveFile("send.eml"), $this->eml());
throw new Exception('Невозможно отправить почту');
throw new Exception('Невозможно отправить почту');
}
return $result;
}
function clear(): void
{
function clear() {
foreach ($this->attachment as $key => &$value) {
unset($this->attachment[$key]);
unset($this->attachment[$key]);
}
$this->attachment = [];
}

View file

@ -5,11 +5,8 @@ use PHPMailer\PHPMailer\PHPMailer;
class MailAlt
{
/** @var PHPMailer */
public $mailer;
/** @var string */
public $_notify;
/** @var string */
public $encoding;
function __construct() {
@ -20,65 +17,62 @@ class MailAlt
/**
* Установка отправителя
*/
function from(string $name): void
function from($name)
{
$this->mailer->setFrom($name);
}
}
/**
* Установка получателя
*/
function to(string $name): void // recipient
function to($name) // recipient
{
$this->mailer->addAddress($name);
}
function replyTo(string $name): void // recipient
function replyTo($name) // recipient
{
$this->mailer->addReplyTo($name);
$this->mailer->AddReplyTo($name);
}
/**
* Установка получателей копии
* @param string $name
*/
function copy(string $name): void // recipient cc
function copy($name) // recipient cc
{
$this->mailer->addCC($name);
}
function notify(string $notify): void
function notify($notify)
{
$this->_notify = $notify;
}
/**
* Тема письма
* @param string $subject
*/
function subject(string $subject): void
function subject($subject/*: string*/)
{
$this->mailer->Subject = $subject;
$this->mailer->Subject = $subject;
}
/**
* Текст письма
* @param string $text
*/
function setContent(string $text): void
function setContent($text)
{
$this->mailer->Body = $text;
}
function setType(string $text): void
function setType($text)
{
$this->mailer->isHTML($text == 'text/html');
}
/**
* Кодировка текста в письме
*/
function setEncoding(string $encoding): void
function setEncoding($encoding)
{
$this->encoding = $encoding;
}
@ -86,20 +80,20 @@ class MailAlt
/**
* Добавление вложения из файла
*/
function addAttachment(string $filename, ?string $name = null): void
function addAttachment($filename, $name = null)
{
$this->mailer->addAttachment($filename, $name);
}
/**
* Отправка почты
* Отправка почты
*/
function send(): bool
function send()
{
return $this->mailer->send();
}
function eml(): string {
function eml() {
return $this->mailer->getSentMIMEMessage();
}
}

View file

@ -2,9 +2,6 @@
namespace ctiso\Model;
/**
* @property \ctiso\Database $db
*/
abstract class BaseMapper {
function getAllAsOptions(): array {
return [];

View file

@ -1,21 +1,17 @@
<?php
namespace ctiso\Model;
use ctiso\Registry;
use ctiso\Database;
use ctiso\Role\User;
use ctiso\Registry,
ctiso\Database,
ctiso\Role\User;
class Factory
{
/** @var Database */
public $db;
/** @var ?Registry */
public $config;
/** @var ?User */
public $user;
public function __construct(Database $db, ?Registry $config = null, ?User $user = null)
public function __construct(Database $db, Registry $config = null, User $user = null)
{
$this->db = $db;
$this->config = $config;

View file

@ -4,27 +4,24 @@ namespace ctiso;
class Numbers
{
static function roman(float $i): float
static function roman($i)
{
return 0;
}
static function decimal(float $i): float
static function decimal($i)
{
return $i;
}
/**
* @param array $array
* @return array
*/
static function prefix(callable $prefix, array $array)
static function prefix($prefix, array $array, $key = false)
{
$result = [];
$count = count($array);
for ($i = 0; $i < $count; $i++) {
$result [] = call_user_func($prefix, $i + 1) . '. ' . $array[$i];
$result [] = call_user_func($prefix, $i + 1) . '. ' . $array[$i];
}
return $result;
}
}

View file

@ -12,16 +12,14 @@ class Path
{
const SEPARATOR = "/";
protected array $path = [];
protected array $url = [];
protected bool $absolute = false;
protected $path = array();
protected $url = array();
protected $absolute = false;
/**
* @param string $path Путь (Тип указан в doccomments т.к откудато приходит null)
*/
public function __construct($path = '')
{
$this->url = parse_url($path) ?: [];
//assert(is_string($path));
$this->url = parse_url($path ?? '');
if (isset($this->url['path'])) {
$path = $this->url['path'];
@ -38,16 +36,16 @@ class Path
}
}
static function factory(string $path): Path {
static function factory($path) {
return new Path($path);
}
public function getParts(): array
public function getParts()
{
return $this->path;
}
public static function normalize(string $pathName): string
public static function normalize($pathName)
{
$path = new Path($pathName);
return $path->__toString();
@ -55,12 +53,12 @@ class Path
/**
* Базовое имя
* @param string $path
* @return string
* @param $path
* @return mixed
*/
public static function basename($path)
{
$list = preg_split('#\\\\|/#s', $path) ?: [''];
$list = preg_split('#\\\\|/#s', $path);
return end($list);
}
@ -75,12 +73,6 @@ class Path
return pathinfo($fileName, PATHINFO_EXTENSION);
}
/**
* Проверяет расширение файла
* @param string $fileName Полное имя файла
* @param string|array $extension Расширение файла
* @return bool
*/
static function isType($fileName, $extension)
{
if (is_array($extension)) {
@ -92,11 +84,17 @@ class Path
/**
* Полное имя файла без расширения
*
* @param string $fileName Имя файла
*
* @return string
*/
static function skipExtension(string $fileName): string
static function skipExtension($fileName)
{
assert(is_string($fileName));
$path = pathinfo($fileName);
if (!isset($path['dirname']) || $path['dirname'] === ".") {
if ($path['dirname'] === ".") {
return $path['filename'];
} else {
return self::join($path['dirname'], $path['filename']);
@ -107,10 +105,13 @@ class Path
* Возвращает имя файла без расширения
*
* @param string $fileName Полное имя файла
*
* @return string
*/
static function getFileName(string $fileName)
static function getFileName($fileName)
{
assert(is_string($fileName));
return pathinfo($fileName, PATHINFO_FILENAME);
}
@ -120,18 +121,20 @@ class Path
* Преобразует строку пути в массив
*
* @param string $path Путь
* @return list<string>
*
* @return array
*/
public static function listFromString(string $path): array
public static function listFromString($path)
{
assert(is_string($path));
$list = preg_split('#\\\\|/#s', $path);
return $list ?: [];
return $list;
}
/**
* Преобразует относительный путь в абсолютный
* @param array<string> $path Путь
* @return array<string>
*/
public static function optimize($path) //
{
@ -151,10 +154,8 @@ class Path
return $result;
}
/**
* Сравнение двух путей на равентство
*/
public function equal(Path $path): bool
// Сравнение двух путей на равентство
public function equal($path/*: Path*/)
{
$count = count($this->path);
if ($count == count($path->path)) {
@ -168,29 +169,27 @@ class Path
return false;
}
/**
* Преобразует путь в строку
* @param array{ host?: string, path?: string, query?: string, fragment?: string, scheme?: string, user?: string, pass?: string, port?: int} $path Путь
*/
public static function makeUrl($path): string
public static function makeUrl($path)
{
$slash = (isset($path['host']) && isset($path['path']) && (strlen($path['path']) > 0) && ($path['path'][0] != '/')) ? '/' : '';
$scheme = isset($path['scheme']) ? $path['scheme'] . ':/' : '';
$user = isset($path['user']) ? $path['user'] . (isset($path['pass']) ? ':' . $path['pass'] : '') . '@' : '';
$slash = (isset($path['host']) && (strlen($path['path']) > 0) && ($path['path'][0] != '/')) ? '/' : '';
$port = isset($path['port']) ? ':' . $path['port'] : '';
$host = isset($path['host']) ? ('/' . $user . $path['host'] . $port) : '';
$query = isset($path['query']) ? '?' . $path['query'] : '';
$fragment = isset($path['fragment']) ? '#' . $path['fragment'] : '';
return $scheme . $host . $slash . ($path['path'] ?? '') . $query . $fragment;
return (isset($path['scheme']) ? $path['scheme'] . ':/' : '')
. (isset($path['host']) ? ('/'
. (isset($path['user']) ? $path['user'] . (isset($path['pass']) ? ':' . $path['pass'] : '') . '@' : '')
. $path['host']
. (isset($path['port']) ? ':' . $path['port'] : '')) : '')
. $slash
. $path['path']
. (isset($path['query']) ? '?' . $path['query'] : '')
. (isset($path['fragment']) ? '#' . $path['fragment'] : '');
}
/**
* Преобразует путь в строку
*
* @return string
*/
public function __toString(): string
public function __toString()
{
$result = (($this->absolute) ? '/' : '') . implode(self::SEPARATOR, $this->path);
$this->url['path'] = $result;
@ -200,9 +199,11 @@ class Path
/**
* Проверяет является ли папка родительской для другой папки
*
* @param Path $path
* @parma Path $path
*
* @return boolean
*/
public function isParent($path): bool
public function isParent($path/*: Path*/)
{
if (isset($this->url['host']) && isset($path->url['host'])
&& ($this->url['host'] != $path->url['host'])) return false;
@ -219,7 +220,7 @@ class Path
return false;
}
public static function _isParent(string $path1, string $path2): bool
public static function _isParent($path1, $path2)
{
$path = new Path($path1);
return $path->isParent(new Path($path2));
@ -229,6 +230,7 @@ class Path
* Находит путь относительно текущего путя
*
* @param string $name Полный путь к файлу
*
* @return string Относительный путь к файлу
*/
public function relPath($name)
@ -243,12 +245,7 @@ class Path
return $path->__toString();
}
/**
* Вычисляет относительный путь в виде строки
* @param string $rpath Путь относительно которого вычисляется относительный путь
* @param string $lpath Путь к которому вычисляется относительный путь
* @return string Относительный путь
*/
// Вычисляет относительный путь в виде строки
static function relative($rpath, $lpath) {
// Нужно проверять диск!!
$self = new Path($rpath);
@ -274,10 +271,6 @@ class Path
return implode("/", $result);
}
/**
* @param string $path
* @return string
*/
public function append($path)
{
$base = $this->__toString();
@ -287,10 +280,11 @@ class Path
/**
* Обьединяет строки в Path соединяя необходимым разделителем
* fixme не обрабатывает параметры урла, решение Path::join(SITE_WWW_PATH) . '?param=pampam'
* @param string ...$args
* @return string
*/
static function fromJoin(...$args) {
static function fromJoin($_rest) {
$args = func_get_args();
$result = [];
$parts0 = new Path(array_shift($args));
$result [] = $parts0->getParts();
@ -308,30 +302,29 @@ class Path
/**
* Обьединяет строки в строку пути соединяя необходимым разделителем
* fixme не обрабатывает параметры урла, решение Path::join(SITE_WWW_PATH) . '?param=pampam'
* @param string ...$args
* @return string
*/
static function join(...$args)
static function join($_rest)
{
$args = func_get_args();
$path = call_user_func_array([self::class, "fromJoin"], $args);
return self::makeUrl($path->url);
}
// Проверка структуры имени файла
static function checkName(string $name, string $extension): bool
static function checkName($name, $extension)
{
return (strlen(pathinfo($name, PATHINFO_FILENAME)) > 0) && (pathinfo($name, PATHINFO_EXTENSION) == $extension);
}
static function isCharName(string $char): bool
static function isCharName($char)
{
$ch = ord($char);
return ((($ch >= ord('a')) && ($ch <= ord('z'))) || (strpos('-._', $char) !== false) || (($ch >= ord('0')) && ($ch <= ord('9'))));
}
// Проверка имени файла
static function isName(string $name): bool
{
static function isName($name) {
if (strlen(trim($name)) == 0) {
return false;
}
@ -343,7 +336,7 @@ class Path
return true;
}
public function isAbsolute(): bool
public function isAbsolute()
{
return $this->absolute;
}
@ -352,6 +345,7 @@ class Path
* Подбирает новое временное имя для файла
*
* @param string $dst Предпологаемое имя файла
*
* @return string Новое имя файла
*/
static function resolveFile($dst)
@ -371,12 +365,12 @@ class Path
/**
* Список файлов в директории
*
* @param ?string[] $allow массив расширений для файлов
* @param string[] $ignore массив имен пааок которые не нужно обрабатывать
* @param array $allow массив расширений для файлов
* @param array $ignore массив имен пааок которые не нужно обрабатывать
*
* @return string[]
* @returnarray
*/
public function getContent(?array $allow = null, array $ignore = [])
public function getContent($allow = null, $ignore = [])
{
$ignore = array_merge([".", ".."], $ignore);
return self::fileList($this->__toString(), $allow, $ignore);
@ -385,12 +379,12 @@ class Path
/**
* Обьединяет строки в путь соединяя необходимым разделителем
*
* @param ?string[] $allow массив расширений разрешеных для файлов
* @param string[] $ignore массив имен папок которые не нужно обрабатывать
* @param array $allow массив расширений разрешеных для файлов
* @param array $ignore массив имен пааок которые не нужно обрабатывать
*
* @return string[]
* @return array
*/
function getContentRec(?array $allow = null, array $ignore = [])
function getContentRec($allow = null, $ignore = [])
{
$result = [];
$ignore = array_merge([".", ".."], $ignore);
@ -398,16 +392,8 @@ class Path
return $result;
}
/**
* Список файлов в директории
*
* @param string $base Базовый путь
* @param ?string[] $allow массив расширений для файлов
* @param string[] $ignore массив имен папок которые не нужно обрабатывать
*
* @return string[]
*/
protected static function fileList(string $base, ?array &$allow, array &$ignore): array
// Использовать SPL ???
protected static function fileList($base, &$allow, &$ignore)
{
if ($base == '') $base = '.';
$result = [];
@ -432,7 +418,7 @@ class Path
return $result;
}
protected static function fileListAll(array &$result, string $base, ?array &$allow, array &$ignore): void
protected static function fileListAll(&$result, $base, &$allow, &$ignore)
{
$files = self::fileList($base, $allow, $ignore);
foreach ($files as $name) {
@ -452,7 +438,7 @@ class Path
*
* @return void
*/
static function prepare(string $dst, bool $filename = true)
static function prepare($dst, $filename = true)
{
if ($filename) {
$path_dst = pathinfo($dst, PATHINFO_DIRNAME);
@ -473,7 +459,7 @@ class Path
*
* @return string
*/
static function updateRelativePathOnFileMove(string $relativePath, string $srcFile, string $dstFile) {
static function updateRelativePathOnFileMove($relativePath, $srcFile, $dstFile) {
$srcToDst = self::relative($srcFile, $dstFile);
return self::normalize(self::join($srcToDst, $relativePath));
}
@ -488,7 +474,7 @@ class Path
*
* @return string
*/
static function updateRelativePathOnDirectoryMove(string $relativePath, string $fileDir, string $srcDir, string $dstDir) {
static function updateRelativePathOnDirectoryMove($relativePath, $fileDir, $srcDir, $dstDir) {
$relativePath = self::normalize($relativePath);
$fileDir = self::normalize($fileDir);
$srcDir = self::normalize($srcDir);

View file

@ -3,74 +3,51 @@
/**
* Преобразование типов !!! Пересмотреть использование классов!!
* Класс преобразования типа значения поля класса в тип поля таблицы
* @package system
*/
namespace ctiso;
class Primitive {
/**
* @param mixed $value
*/
public static function to_varchar($value): string
// varchar
public static function to_varchar($value)
{
return ((string) $value);
}
/**
* @param mixed $value
* @return mixed
*/
public static function from_varchar($value)
{
return $value;
}
/**
* @param mixed $value
*/
public static function to_bool($value): bool
// int
public static function to_bool($value)
{
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
return filter_var($value, FILTER_VALIDATE_BOOLEAN);//(int)((bool) $value);
}
/**
* Преобразование значения в булевое значение
* @param string $value
* @return bool
*/
public static function from_bool($value): bool
public static function from_bool($value)
{
return ((bool) $value);
}
/**
* Преобразование значения в целое число
* @param string $value
* @return int
*/
public static function to_int($value): int
// int
public static function to_int($value)
{
return ((int) $value);
}
/**
* @param mixed $value
*/
public static function from_int($value): string
public static function from_int($value)
{
return ((string) $value);
}
/**
* Преобразование даты dd/mm/yy в unix timestamp
* @param string $value
* @return int
*/
public static function to_date($value): int
// date
public static function to_date($value)
{
$result = 0;
$tmp = explode("/", $value ?? '', 3);
if (count($tmp) != 3) {
return $result;
}
@ -81,39 +58,29 @@ class Primitive {
if ($month != 0 && $day != 0 && $year != 0) {
if (checkdate($month, $day, $year)) {
return mktime(0, 0, 0, $month, $day, $year) ?: 0;
return mktime(0, 0, 0, $month, $day, $year);
} else {
return 0;
}
}
return $result;
}
/**
* Преобразование даты ISO 8601 в unix timestamp
* @param string $value
* @return int
*/
public static function to_datetime($value): int
public static function to_datetime($value)
{
$result = 0;
$tmp = [];
if (preg_match('/(\d+)-(\d+)-(\d+)T(\d+):(\d+)Z/', $value, $tmp)) {
if (checkdate((int)$tmp[2], (int)$tmp[3], (int)$tmp[1])) {
$result = mktime((int)$tmp[4], (int)$tmp[5], 0, (int)$tmp[2], (int)$tmp[3], (int)$tmp[1]) ?: 0;
$result = mktime((int)$tmp[4], (int)$tmp[5], 0, (int)$tmp[2], (int)$tmp[3], (int)$tmp[1]);
}
}
return $result;
}
/**
* Преобразование даты в формат dd/mm/yyyy
* @param int $value
* @return string
*/
public static function from_date($value): string
public static function from_date($value)
{
if ($value > 0) {
return date("d/m/Y", $value);
@ -121,12 +88,7 @@ class Primitive {
return '';
}
/**
* Преобразование даты в формат ISO 8601
* @param int $value
* @return string
*/
public static function from_datetime($value): string
public static function from_datetime($value)
{
if ($value > 0) {
return date("Y-m-d\TH:i\Z", $value);
@ -135,45 +97,24 @@ class Primitive {
}
/**
* @deprecated
* @template T
* @param T $value
* @return T
*/
// secure
public static function to_secure($value)
{
// Значение приабразуется во время сохранения в базе данных
return $value;
}
/**
* @deprecated
* @template T
* @param T $value
* @return T
*/
public static function from_secure($value)
{
return $value;
}
/**
* Преобразование значения в массив
* @param mixed $value
* @return array
*/
public static function to_array($value)
// array
public static function to_array($value)
{
return (is_array($value)) ? $value : [];
}
/**
* @deprecated
* @template T
* @param T $value
* @return T
*/
public static function from_array($value)
{
return $value;

View file

@ -2,46 +2,28 @@
namespace ctiso;
/**
* str_getcsv
* @param string $input
* @param string $delimiter
* @param string $enclosure
* @param string $escape
* @return array|false
*/
function str_getcsv($input, $delimiter = ",", $enclosure = '"', $escape = "\\")
{
$fiveMBs = 1024;
$fp = fopen("php://temp/maxmemory:$fiveMBs", 'r+');
$data = [];
if (is_resource($fp)) {
fputs($fp, $input);
rewind($fp);
$data = fgetcsv($fp, 1000, $delimiter, $enclosure);
fclose($fp);
}
return $data;
if (!function_exists('str_getcsv')) {
function str_getcsv($input, $delimiter = ",", $enclosure = '"', $escape = "\\") {
$fiveMBs = 1024;
$fp = fopen("php://temp/maxmemory:$fiveMBs", 'r+');
$data = '';
if (is_resource($fp)) {
fputs($fp, $input);
rewind($fp);
$data = fgetcsv($fp, 1000, $delimiter, $enclosure);
fclose($fp);
}
return $data;
}
}
/**
* process_exists
* @param int $pid
* @return bool
*/
function process_exists($pid)
{
function process_exists($pid) {
if (PHP_OS == 'WINNT') {
$content = shell_exec("tasklist.exe /NH /FO CSV");
if (!$content) {
return false;
}
$processes = explode("\n", $content);
foreach ($processes as $process) {
$processes = explode("\n", shell_exec("tasklist.exe /NH /FO CSV"));
foreach($processes as $process) {
if ($process != "") {
$csv = str_getcsv($process);
if ($csv && $pid == $csv[1]) return true;
if ($pid == $csv[1]) return true;
}
}
return false;
@ -50,19 +32,13 @@ function process_exists($pid)
}
}
/**
* create_single_proces
* @param string $fpid
* @param callable $fn
* @return int
*/
function create_single_process($fpid, $fn)
{
function create_single_proces($fpid, $fn) {
if (file_exists($fpid)) {
if (process_exists((int)file_get_contents($fpid))) {
return 1;
print_r(realpath($fpid));
if (process_exists(file_get_contents($fpid))) {
return 1;
}
}
}
call_user_func($fn);
return 0;
}

View file

@ -5,58 +5,47 @@ use ctiso\File,
Exception;
class Registry {
/** @var array<string, array{path: string, data: mixed}> */
private array $namespace = [];
public $namespace = [];
public $data;
function importFile(string $namespace, ?string $filePath = null): void {
function importFile($namespace, $filePath = null) {
$data = json_decode(File::getContents($filePath), true);
$data['_file'] = $filePath;
$this->namespace[$namespace] = [
'path' => $filePath,
'data' => $data
];
];
}
function importArray(string $namespace, array $data = []): void {
function importArray($namespace, $data = []) {
if (isset($this->namespace[$namespace])) {
$data = array_merge($this->namespace[$namespace]['data'], $data);
}
}
$this->namespace[$namespace] = [
'path' => null,
'data' => $data
];
];
}
/**
* @param string $ns
* @param string $key
* @return mixed
* @throws Exception
*/
public function get(string $ns, string $key) {
public function get($ns, $key) {
if (isset($this->namespace[$ns]['data'][$key])) {
return $this->namespace[$ns]['data'][$key];
}
throw new Exception('Unknown key ' . $ns . '::' . $key);
}
/**
* @param string $ns
* @param string $key
* @return mixed|null
*/
public function getOpt(string $ns, string $key) {
public function getOpt($ns, $key) {
if (isset($this->namespace[$ns]['data'][$key])) {
return $this->namespace[$ns]['data'][$key];
}
return null;
}
public function has(string $ns, string $key): bool {
public function has($ns, $key) {
return isset($this->namespace[$ns]['data'][$key]);
}
function set(string $ns, string $key, mixed $value): void {
function set($ns, $key, $value) {
$this->namespace[$ns]['data'][$key] = $value;
}
}

View file

@ -1,47 +1,41 @@
<?php
namespace ctiso\Role;
use ctiso\Database,
ctiso\Database\Statement;
use ctiso\Database;
use ctiso\Database\Statement;
use ctiso\Database\PDOStatement;
// Класс должен быть в библиотеке приложения
// Класс должен быть в библиотеке приложения
class User implements UserInterface
{
const LIFE_TIME = 1800; // = 30min * 60sec;
public string $fullname;
public string $name;
/** @var string */
public $fullname;
public $name;
public $access;
public string $password;
/** @var int */
public $password;
public $id;
public Database $db;
public array $groups;
public $db;
public $groups;
function __construct(Database $db, array $groups) {
function __construct($db, $groups) {
$this->db = $db;
$this->groups = $groups;
}
public function setDB(Database $db): void {
public function setDB(Database $db) {
$this->db = $db;
}
public function getName(): string {
public function getName() {
return $this->name;
}
/**
* @return bool
*/
function isLogged() {
return \ctiso\Filter\Authorization::isLogged();
}
public function getUserByQuery(Statement $stmt): ?PDOStatement
public function getUserByQuery(Statement $stmt)
{
$result = $stmt->executeQuery();
if ($result->next()) {
@ -50,38 +44,32 @@ class User implements UserInterface
$this->id = $result->getInt('id_user');
$this->password = $result->getString('password');
$this->fullname = implode(' ', [
$result->getString('surname'),
$result->getString('firstname'),
$result->getString('surname'),
$result->getString('firstname'),
$result->getString('patronymic')]);
return $result;
}
return null;
}
/**
* @param PDOStatement $result
* @return string
*/
function getUserPassword($result) {
return $result->get('password');
}
public function getUserByLogin(string $login): ?PDOStatement
public function getUserByLogin($login)
{
$stmt = $this->db->prepareStatement("SELECT * FROM users WHERE login = ?");
$stmt->setString(1, $login);
$result = $this->getUserByQuery($stmt);
if ($result) {
if ($result) {
$time = time();
$id = $this->id;
$this->db->executeQuery(
"UPDATE users SET lasttime = :time WHERE id_user = :id",
['time' => $time, 'id' => $id]); // Время входа
$this->db->executeQuery("UPDATE users SET lasttime = $time WHERE id_user = $id"); // Время входа
}
return $result;
}
public function getUserById(int $id): ?PDOStatement
public function getUserById($id)
{
$stmt = $this->db->prepareStatement("SELECT * FROM users WHERE id_user = ?");
$stmt->setInt(1, $_SESSION ['access']);
@ -89,37 +77,25 @@ class User implements UserInterface
if ($result) {
$lasttime = $result->getInt('lasttime');
$time = time();
if ($time - $lasttime > self::LIFE_TIME) return null; // Вышло время сессии
if ($time - $lasttime > self::LIFE_TIME) return null; // Вышло время сессии
$id = $this->id;
}
return $result;
}
/**
* @param string $random
* @param PDOStatement $result
* @return PDOStatement|bool
*/
function setSID(string $random, $result)
{
return $this->db->executeQuery("UPDATE users SET sid = :sid, trie_count = 0 WHERE id_user = :user", [
'user' => $result->getInt('id_user'),
'sid' => $random
]);
function setSID($random, $result) {
return $this->db->executeQuery("UPDATE users SET sid = '$random', trie_count = 0 WHERE id_user = " . $result->getInt('id_user'));
}
function resetTries(string $login): void {
function resetTries($login) {
$this->db->executeQuery(
"UPDATE users SET trie_count = :count WHERE login = :login",
['count' => 0, 'login' => $login]
);
}
function updateTries(string $login): void {
function updateTries($login) {
$user = $this->db->fetchOneArray("SELECT id_user, trie_count FROM users WHERE login = :login", ['login' => $login]);
if ($user === false) {
return;
}
$this->db->executeQuery(
"UPDATE users SET trie_time = :cur_time, trie_count = :count WHERE id_user = :id_user",
['cur_time' => time(), 'count' => $user['trie_count']+1, 'id_user' => $user['id_user']]

View file

@ -2,18 +2,11 @@
namespace ctiso\Role;
use ctiso\Database\Statement;
use ctiso\Database\PDOStatement;
interface UserInterface {
function getUserByQuery(Statement $stmt): ?PDOStatement;
function getUserByLogin(string $login): ?PDOStatement;
function getUserById(int $id): ?PDOStatement;
function getName(): string;
/**
* @param string $random
* @param PDOStatement $result
* @return PDOStatement|bool
*/
function setSID(string $random, $result);
function getUserByQuery(Statement $stmt);
function getUserByLogin($login);
function getUserById($id);
function getName();
function setSID($random, $result);
}

View file

@ -3,7 +3,7 @@
namespace ctiso;
class Security {
static function generatePassword(int $length = 9, int $strength = 0): string {
static function generatePassword($length = 9, $strength = 0) {
$vowels = 'aeuy';
$consonants = 'bdghjmnpqrstvz';
if ($strength & 1) {
@ -18,7 +18,7 @@ class Security {
if ($strength & 8) {
$consonants .= '@#$%';
}
$password = '';
$alt = time() % 2;
for ($i = 0; $i < $length; $i++) {

View file

@ -2,9 +2,9 @@
namespace ctiso;
class Session
class Session
{
function get(string $key): mixed
function get($key)
{
if (isset($_SESSION[$key])) {
return $_SESSION[$key];
@ -12,27 +12,26 @@ class Session
return null;
}
function set(string|array $key, mixed $value): void
function set($key, $value)
{
if (is_array($key)) {
$className = get_class($key[0]);
$_SESSION[strtolower($className ?: '')][$key[1]] = $value;
$_SESSION[strtolower(get_class($key[0]))][$key[1]] = $value;
} else {
$_SESSION[$key] = $value;
}
}
function clean(string $key): void
function clean($key)
{
unset($_SESSION[$key]);
}
function start(): void
function start()
{
@session_start();
}
function stop(): void
function stop()
{
session_destroy();
}

View file

@ -5,30 +5,21 @@ use ctiso\File,
Exception;
/**
* Класс реестра
* Класс реестра
* Реестр организован как ассоциативный многомерный массив
* array( 'name1' => parameters1, 'name2' => parameters1, ... )
*
* name1, name2 ... - Имена модулей
* name1, name2 ... - Имена модулей
* parameters1, parameters1 - Массивы с параметрами модуля
* Имя необходимо чтобы потом легко было удалить ненужные ветки дерева
*/
class Settings
{
/** @var array */
public $data = [];
/** @var string */
protected $file;
/** @var string */
protected $format = 'php';
/** @var bool */
protected $is_read = false;
/**
* Конструктор
* @param string $file Путь к файлу
* @param 'php'|'json'|false $format Формат файла
*/
public function __construct ($file = null, $format = false)
{
$fileFormat = ['theme' => 'json'];
@ -42,7 +33,7 @@ class Settings
* Чтение настроек из файла
* @return Boolean
*/
public function read(): bool
public function read()
{
if (!file_exists ($this->file)) {
$this->is_read = true;
@ -67,19 +58,18 @@ class Settings
/**
* Запись ключа в реестр (Реестр это многомерный массив)
* @param array $key Путь к значению ключа
* @param mixed $value Значение
* @return void
*/
public function writeKey(array $key, $value)
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])) {
@ -93,10 +83,8 @@ class Settings
/**
* Обновляет массив в соответствии со значением
* @param array $data Массив
* @param mixed $value Значение
*/
protected function merge(array &$data, $value): void
protected function merge(array &$data, $value)
{
foreach ($value as $key => $subvalue) {
if (is_array($subvalue)) {
@ -109,23 +97,17 @@ class Settings
}
/**
* Чтение ключа из реестра
* Чтение ключа из реестра
* @param array $key Путь к значению ключа
* @return mixed
*/
public function readKey(array $key)
{
return $this->readKeyData($key, $this->data);
}
/**
* Чтение ключа из реестра
* @param array $key Путь к значению ключа
* @param array $data
* @return mixed
*/
protected function readKeyData(array $key, $data)
{
// assert(count($key) >= 1);
while (count($key) > 1) {
$name = array_shift($key);
if (isset($data[$name])) {
@ -133,8 +115,9 @@ class Settings
} else {
return null;
}
}
}
// assert(count($key) == 1);
$name = array_shift($key);
if (isset($data[$name])) {
return $data[$name];
@ -146,10 +129,9 @@ class Settings
/**
* Чтение ключа из реестра (Собирает все ключи с определенным значением во всех модулях)
* @param mixed $key Путь к значению ключа внутри модуля
* @return array
*/
public function readKeyList(...$key)
{
{
$result = [];
foreach ($this->data as $name => $value) {
$output = $this->readKeyData($key, $value);
@ -160,17 +142,13 @@ class Settings
return $result;
}
/**
* Удаление ключа из реестра
* @param string $name Имя ключа
*/
public function removeKey($name): void
public function removeKey($name)
{
unset($this->data[$name]);
}
public function removeNode(array $key): void
public function removeNode(array $key)
{
$data = &$this->data;
while (count($key) > 1) {
@ -180,11 +158,11 @@ class Settings
$name = array_shift($key);
unset($data[$name]);
}
/**
* Запись настроек в файл (Может переименовать в store)
*
* @param string $file
* @param File $file
* @return void
*/
public function write($file = null)
@ -199,49 +177,28 @@ class Settings
$result = var_export($this->data, true);
$result = "<?php\nreturn ".$result.";\n?>";
}
file_put_contents(($file) ? $file : $this->file, $result);
file_put_contents (($file) ? $file : $this->file, $result);
}
/**
* Установка значения ключа
* @param string $key Ключ
* @param mixed $value Значение
*/
public function set($key, $value): void {
public function set($key, $value) {
$this->data[$key] = $value;
}
/**
* Получение значения ключа
* @param string $key Ключ
* @param mixed $default Значение по умолчанию
* @return mixed
*/
public function get($key, $default = null)
{
return isset($this->data[$key]) && $this->data[$key] != '' ? $this->data[$key] : $default;
}
/**
* Получение всех данных
* @return array
*/
function export() {
return $this->data;
}
/**
* Импорт данных
* @param array $data Данные
* @return void
*/
function import($data) {
$this->data = $data;
}
/**
* Список модулей/ключей
* @return array
* Список модулей/ключей
*/
public function getKeys()
{
@ -250,8 +207,6 @@ class Settings
/**
* Проверка наличия ключа
* @param string $name Ключ
* @return bool
*/
public function hasKey($name)
{

View file

@ -6,65 +6,40 @@
* $setup->set('target', 'dst');
* $setup->executeActions('install');
* </code>
*/
*/
namespace ctiso;
use ctiso\Tools\SQLStatementExtractor;
use ctiso\Path;
use ctiso\File;
use SimpleXMLElement;
class FakeZipArchive {
/** @var string */
public $base;
function open(string $path): void {
function open($path) {
$this->base = $path;
}
/**
* Возвращает содержимое файла
* @param string $file
* @return string
*/
function getFromName($file) {
return File::getContents(Path::join($this->base, $file));
return file_get_contents(Path::join($this->base, $file));
}
}
class Setup
class Setup
{
/** @var array */
protected $actions = [];
/** @var array */
public $context = [];
/** @var string */
protected $actions = array();
public $context = array();
protected $file;
/** @var string */
protected $action;
/** @var SimpleXMLElement */
protected $node;
/** @var array */
protected $stack = [];
/** @var FakeZipArchive */
protected $stack = array();
public $zip;
/** @var string */
public $target;
/** @var string */
public $source;
/**
* @param string $file
*/
public function __construct($file)
{
$this->file = $file;
$node = simplexml_load_file($file);
if ($node === false) {
throw new \RuntimeException("Can't load file $file");
}
$this->node = $node;
$this->node = simplexml_load_file($file);
$this->target = '';
$this->source = '';
@ -81,29 +56,22 @@ class Setup
}
/**
* Регистрация новых действия для установки
* @param string $name
* @param callable $action
* Регистрация новых действия для установки
*/
public function registerAction(string $name, $action): void
public function registerAction($name, $action)
{
$this->actions[$name] = $action;
}
/**
* Установка переменных для шаблона
* @param string $name
* @param mixed $value
*/
public function set(string $name, $value): void
public function set($name, $value)
{
$this->context[$name] = $value;
}
/**
* @return string
*/
function replaceFn(array $matches) {
function replaceFn($matches) {
if (isset($this->context[$matches[2]])) {
$v = $this->context[$matches[2]];
} else {
@ -116,14 +84,14 @@ class Setup
return $v;
}
public function fileContent(string $file, array $tpl): string
{
public function fileContent($file, array $tpl)
{
$result = $this->zip->getFromName($file);
$result = preg_replace_callback('/\{\{\s*(\*?)(\w+)\s*\}\}/', [$this, 'replaceFn'], $result);
return $result;
}
function callAction(string $name, array $attributes): void
function callAction($name, array $attributes)
{
if(isset($this->actions[$name])) {
call_user_func_array($this->actions[$name], $attributes);
@ -132,8 +100,6 @@ class Setup
/**
* Заменяет переменные на их значения в строке
* @param array<string> $match массив совпадения
* @return string
*/
function replaceVariable(array $match)
{
@ -145,55 +111,50 @@ class Setup
/**
* Для всех аттрибутов заменяет переменные на их значения
* @param SimpleXMLElement $attributes аттрибуты
* @return array<string, string>
*/
function resolve(SimpleXMLElement $attributes): array
{
function resolve($attributes)
{
$result = [];
foreach ($attributes as $key => $value) {
$result[$key] = preg_replace_callback("/\\\${(\w+)}/", $this->replaceVariable(...), $value);
$result [$key] = preg_replace_callback("/\\\${(\w+)}/", [$this, 'replaceVariable'], $value);
}
return $result;
}
/**
* Выполняет список действий если для действия не указан аттрибут when то оно выполняется всегда
*
*
* @param string $action специальное действие
* @return void
*/
function executeActions($action = "install")
{
$this->action = $action;
$this->action = $action;
if ($this->stack[count($this->stack) - 1] === false) {
return;
}
/** @var \SimpleXMLElement */
$item = $this->stack[count($this->stack) - 1];
$item/*: \SimpleXMLElement*/ = $this->stack[count($this->stack) - 1];
$root = $item->children();
foreach ($root as $node)
{
{
$attributes = $node->attributes();
array_push($this->stack, $node);
$this->callAction($node->getName(), [$this->resolve($attributes)]);
array_pop($this->stack);
}
}
}
/**
* Копирования файла
* Копирования файла
* preserve - Не переписывать файл если он существует
* template Файл является шаблоном подставить параметры до копирования
* src Исходный файл
* dst Новый файл
*
*
* @param array{preserve?: string, template: string, src: string, dst: string} $attributes
* @return void
*/
public function copyFile(array $attributes)
{
{
$path = $this->targetPath($attributes['dst']);
if (!(file_exists($path) && isset($attributes['preserve']))) {
@ -203,9 +164,9 @@ class Setup
/**
* Создает символическую ссылку на папку/файл
* @param array{target: string, link: string} $attributes
* @param array{target: string, link: string} $attributes
*/
public function makeLink(array $attributes): void
public function makeLink(array $attributes)
{
if (function_exists('symlink')) {
symlink($attributes['target'], $attributes['link']);
@ -213,37 +174,37 @@ class Setup
}
/**
* Подключение файла установки
* Подключение файла установки
* @param array{file: string} $attributes Имя подключаемого файла
*/
public function includeFile(array $attributes): void
public function includeFile(array $attributes)
{
$file = basename($this->file) . "/" . $attributes['file'];
$setup = new Setup($file);
$setup->context = $this->context;
$setup->context = $this->context;
$setup->executeActions();
}
function targetPath(string $s): string {
function targetPath($s) {
return $this->target . '/' . $s;
}
/**
* Создает новую папку
* dst Имя папки
*
* @param array{dst:string} $attributes
* dst Имя папки
*
* @param array{dst:string} $attributes
*/
public function makeDirectory(array $attributes): void
public function makeDirectory(array $attributes)
{
$path = $this->targetPath($attributes['dst']);
if (!file_exists($path)) {
mkdir($path);
}
}
}
function testWhen(array $attributes): void
function testWhen(array $attributes)
{
if (!isset($attributes['test']) || $attributes['test'] == $this->action) {
$this->executeActions($this->action);
@ -251,11 +212,9 @@ class Setup
}
/**
* Выполнение Списка SQL команд из ZIP файла
* @param Database $conn
* @param string $file
*/
function batchSQLZip($conn, $file): void
* Выполнение Списка SQL команд
*/
function batchSQLZip($conn/*: Database*/, $file)
{
$stmtList = SQLStatementExtractor::extract($this->zip->getFromName($file));
foreach ($stmtList as $stmt) {
@ -263,12 +222,7 @@ class Setup
}
}
/**
* Выполнение Списка SQL команд
* @param Database $conn
* @param string $file
*/
static function batchSQL($conn, $file): void
static function batchSQL($conn/*: Database*/, $file)
{
$stmtList = SQLStatementExtractor::extractFile($file);
foreach ($stmtList as $stmt) {

View file

@ -2,23 +2,20 @@
namespace ctiso;
class SortRecord
class SortRecord
{
public string $key;
public int $order;
public $key;
public $mode;
public $order;
function __construct(string $key, bool $order)
{
function __construct($key, $mode, $order)
{
$this->key = $key;
$this->order = ((boolean)($order) === false) ? 1 : -1;
$this->mode = $mode;
}
/**
* @template T
* @param array<string, T> $a
* @param array<string, T> $b
*/
function compare(array $a, array $b): int
function compare($a, $b)
{
if($a[$this->key] == $b[$this->key]) {
return 0;
@ -26,7 +23,7 @@ class SortRecord
return ($a[$this->key] > $b[$this->key]) ? $this->order : -$this->order;
}
function compareKeys(object $a, object $b): int
function compareKeys($a, $b)
{
if($a->{$this->key} == $b->{$this->key}) {
return 0;
@ -34,17 +31,17 @@ class SortRecord
return ($a->{$this->key} > $b->{$this->key}) ? $this->order : -$this->order;
}
function sort(array &$list): bool
function sort(&$list)
{
return usort($list, [$this, 'compare']);
}
function sortKeys(array &$list): bool
function sortKeys(&$list)
{
return usort($list, [$this, 'compareKeys']);
}
function group(array &$list, string $key, array $types): void
function group(&$list, $key, $types)
{
$groups = [];
foreach ($types as $name) {
@ -62,6 +59,6 @@ class SortRecord
foreach ($groups as $value) {
$result = array_merge($result, $value);
}
$list = $result;
$list = $result;
}
}

View file

@ -4,29 +4,26 @@
* Преобразование дерева из модели Plain в массив массивов (Adjacency List)
*/
/**
* Обходит таблицу как дерево
* $fn ($name, $index, $rows, $cc)
* $name Ключ уровня
* $index Значение ключа уровня
* $rows Все столбцы текущго уровня
* $cc Столбцы более низкого уровня
*
* @param Array $level Уровни вложенности
* @param array $table Таблица
* @param Function $fn Функция которая применяется к каждой ветке дерева
*/
namespace ctiso;
use ctiso\Functions;
class TableTree {
/**
* Обходит таблицу как дерево
* $fn ($name, $index, $rows, $cc)
* $name Ключ уровня
* $index Значение ключа уровня
* $rows Все столбцы текущго уровня
* $cc Столбцы более низкого уровня
*
* @param array $level Уровни вложенности
* @param array $table Таблица
* @param callable $fn Функция которая применяется к каждой ветке дерева
* @return array
*/
static function walk($level, $table, $fn) {
if (empty ($level)) {
return $table;
}
if (empty ($level)) return $table;
$name = array_shift ($level);
$keys = Functions::key_unique_values($name, $table);
$data = [];
foreach ($keys as $index) {

View file

@ -4,30 +4,27 @@
* Расширения для PHPTAL для отображения времени и даты
*/
namespace ctiso;
use PHPTAL_Php_TalesInternal;
use ctiso\Controller\SiteInterface;
use ctiso\Controller\Component;
use ctiso\HttpRequest;
use PHPTAL_Tales;
use PHPTAL_TalesRegistry;
use PHPTAL_Php_TalesInternal,
ctiso\Controller\SiteInterface,
ctiso\Controller\Component,
ctiso\HttpRequest,
PHPTAL_Tales,
PHPTAL_TalesRegistry;
class Tales_DateTime implements PHPTAL_Tales
{
static public function date(string $expression, bool $nothrow = false): string
{
static public function date($expression, $nothrow = false) {
return "ctiso\\Tales::phptal_date(".PHPTAL_Php_TalesInternal::path($expression).")";
}
static public function time(string $expression, bool $nothrow = false): string
{
static public function time($expression, $nothrow = false) {
return "ctiso\\Tales::phptal_time(".PHPTAL_Php_TalesInternal::path($expression).")";
}
}
class Tales_Component implements PHPTAL_Tales
{
static public function component(string $expression, bool $nothrow = false): string
static public function component($expression, $nothrow = false)
{
$s = PHPTAL_Php_TalesInternal::string($expression);
return "ctiso\\Tales::phptal_component(" . $s . ")";
@ -36,7 +33,7 @@ class Tales_Component implements PHPTAL_Tales
class Tales_Assets implements PHPTAL_Tales
{
static public function assets(string $expression, bool $nothrow = false): string
static public function assets($expression, $nothrow = false)
{
$s = PHPTAL_Php_TalesInternal::string($expression);
return "ctiso\\Tales::phptal_asset(" . $s . ")";
@ -44,47 +41,45 @@ class Tales_Assets implements PHPTAL_Tales
}
class Tales {
/** @var ?SiteInterface */
static $site;
static $site/*: SiteInterface*/;
static function phptal_date (int $e): string {
static function phptal_date ($e) {
return date("d.m.Y", $e);
}
static function phptal_time (int $e): string {
static function phptal_time ($e) {
return date("H:i", $e);
}
static function phptal_asset(string $s): string {
static function phptal_asset($s) {
self::$site->addStyleSheet($s);
return "";
}
/**
* Функция подключения компонента
* @param string $expression
* @return string
*/
static function phptal_component($expression): string {
static function phptal_component($expression) {
$begin = floatval(microtime(true));
/** @var Component */
$component/*: Component*/ = null;
$component = self::$site->loadComponent($expression);
$req = new HttpRequest();
$req = new HttpRequest();
$result = $component->execute($req);
echo "<!-- ", $expression, ", ", round(floatval(microtime(true)) - $begin, 4), "sec -->";
return $result;
}
static function register(?SiteInterface $site): void {
static function register($site) {
self::$site = $site;
/* Регистрация нового префикса для подключения компонента */
$tales = PHPTAL_TalesRegistry::getInstance();
$tales->registerPrefix('component', [\ctiso\Tales_Component::class, 'component']);
$tales->registerPrefix('date', [\ctiso\Tales_DateTime::class, 'date']);
$tales->registerPrefix('time', [\ctiso\Tales_DateTime::class, 'time']);
$tales->registerPrefix('assets', [\ctiso\Tales_Assets::class, 'assets']);
$tales->registerPrefix('component', ['ctiso\\Tales_Component', 'component']);
$tales->registerPrefix('date', ['ctiso\\Tales_DateTime', 'date']);
$tales->registerPrefix('time', ['ctiso\\Tales_DateTime', 'time']);
$tales->registerPrefix('assets', ['ctiso\\Tales_Assets', 'assets']);
}
}

View file

@ -2,8 +2,6 @@
namespace ctiso\Tools;
use GdImage;
class Drawing
{
const ALIGN_LEFT = "left";
@ -12,61 +10,26 @@ class Drawing
const ALIGN_CENTER = "center";
const ALIGN_RIGHT = "right";
/**
* @param GdImage $image
* @param int $left
* @param int $top
* @param int $width
* @param int $height
* @param list<int<0, 255>> $rgb
*/
static function drawRectangle(GdImage &$image, int $left, int $top, int $width, int $height, array $rgb): void
static function drawrectnagle(&$image, $left, $top, $width, $height, $rgb)
{
$color = imagecolorallocate($image, $rgb[0], $rgb[1], $rgb[2]);
if ($color === false) {
throw new \RuntimeException("Can't allocate color");
}
$right = $left + $width;
$bottom = $top + $height;
imageline($image, $left, $top, $right, $top, $color);
imageline($image, $right, $top, $right, $bottom, $color);
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);
imageline($image, $left, $top, $left, $bottom, $color);
}
/**
* http://ru2.php.net/imagettftext
*
* @param GdImage $image
* @param int $size
* @param float $angle
* @param int $left
* @param int $top
* @param int $color
* @param string $font
* @param string $text
* @param int $max_width
* @param int $max_height
* @param string $align
* @param string $valign
*/
static function imagettftextbox(
GdImage &$image,
int $size,
float $angle,
$left,
$top,
$color,
$font,
$text,
$max_width,
$max_height,
$align,
$valign
): float {
// 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));
static function imagettftextbox(&$image, $size, $angle, $left, $top, $color, $font, $text,
$max_width, $max_height, $align, $valign)
{
// 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 = [];
@ -84,9 +47,6 @@ class Drawing
for ($i = 0; $i < $count; $i++) {
$item = $words[$i];
$dimensions = imagettfbbox($size, $angle, $font, $current_line . ($first_word ? '' : ' ') . $item);
if ($dimensions === false) {
continue;
}
$line_width = $dimensions[2] - $dimensions[0];
$line_height = $dimensions[1] - $dimensions[7];
@ -142,15 +102,20 @@ class Drawing
return $largest_line_height * count($lines);
}
function imagettftextSp(GdImage $image, float $size, float $angle, int $x, int $y, int $color, string $font, string $text, int $spacing = 0): void
function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0)
{
if ($spacing == 0) {
if ($spacing == 0)
{
imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
} else {
}
else
{
$temp_x = $x;
for ($i = 0; $i < mb_strlen($text); $i++) {
for ($i = 0; $i < mb_strlen($text); $i++)
{
$bbox = imagettftext($image, $size, $angle, $temp_x, $y, $color, $font, $text[$i]);
$temp_x += $spacing + ($bbox !== false ? ($bbox[2] - $bbox[0]) : 0);
$temp_x += $spacing + ($bbox[2] - $bbox[0]);
}
}
}

View file

@ -2,15 +2,9 @@
namespace ctiso\Tools;
use GdImage;
class Image
{
/**
* @param string $uri
* @return GdImage|false
*/
static function load($uri): GdImage|false
{
static function load($uri)
{
$e = strtolower(pathinfo($uri, PATHINFO_EXTENSION));
switch ($e) {
@ -18,32 +12,24 @@ class Image
case 'jpeg': case 'jpg': return imagecreatefromjpeg($uri);
case 'gif': return imagecreatefromgif($uri);
}
return false;
}
static function fit(GdImage $image, int $prewidth, int $preheight, bool $force = true): GdImage|false
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 = max(1, (int)($width * $percent));
$new_height = max(1, (int)($height * $percent));
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;
}
/**
* @param GdImage $image
* @param string $uri
* @return bool
*/
static function save($image, $uri): bool
static function save($image, $uri)
{
$e = strtolower(pathinfo($uri, PATHINFO_EXTENSION));
switch ($e) {
@ -51,6 +37,5 @@ class Image
case 'png': imagepng($image, $uri); break;
case 'gif': imagegif($image, $uri); break;
}
return false;
}
}

View file

@ -18,7 +18,7 @@
* and is licensed under the LGPL. For more information please see
* <http://creole.phpdb.org>.
*/
/**
* Static class for extracting SQL statements from a string or file.
*
@ -26,157 +26,142 @@
* @version $Revision: 1.5 $
* @package creole.util.sql
*/
namespace ctiso\Tools;
use Exception;
class SQLStatementExtractor
{
/** @var string */
class SQLStatementExtractor {
protected static $delimiter = ';';
/**
* Get SQL statements from file.
*
*
* @param string $filename Path to file to read.
* @return array SQL statements
*/
public static function extractFile($filename)
{
public static function extractFile($filename) {
$buffer = file_get_contents($filename);
if ($buffer !== false) {
return self::extractStatements(self::getLines($buffer));
return self::extractStatements(self::getLines($buffer));
}
throw new Exception("Unable to read file: " . $filename);
}
/**
* Extract statements from string.
*
*
* @param string $buffer
* @return array
*/
public static function extract($buffer)
{
public static function extract($buffer) {
return self::extractStatements(self::getLines($buffer));
}
/**
* Extract SQL statements from array of lines.
*
* @param string[] $lines Lines of the read-in file.
* @return string[] SQL statements
* @param array $lines Lines of the read-in file.
* @return array
*/
protected static function extractStatements($lines)
{
protected static function extractStatements($lines) {
$statements = [];
$sql = "";
foreach($lines as $line) {
$line = trim($line);
if (self::startsWith("//", $line) ||
self::startsWith("--", $line) ||
self::startsWith("#", $line)) {
continue;
}
if (strlen($line) > 4 && strtoupper(substr($line,0, 4)) == "REM ") {
continue;
}
foreach ($lines as $line) {
$line = trim($line);
$sql .= " " . $line;
$sql = trim($sql);
if (
self::startsWith("//", $line) ||
self::startsWith("--", $line) ||
self::startsWith("#", $line)
) {
continue;
// SQL defines "--" as a comment to EOL
// and in Oracle it may contain a hint
// so we cannot just remove it, instead we must end it
if (strpos($line, "--") !== false) {
$sql .= "\n";
}
if (self::endsWith(self::$delimiter, $sql)) {
$statements[] = self::substring($sql, 0, strlen($sql)-1 - strlen(self::$delimiter));
$sql = "";
}
}
if (strlen($line) > 4 && strtoupper(substr($line, 0, 4)) == "REM ") {
continue;
}
$sql .= " " . $line;
$sql = trim($sql);
// SQL defines "--" as a comment to EOL
// and in Oracle it may contain a hint
// so we cannot just remove it, instead we must end it
if (strpos($line, "--") !== false) {
$sql .= "\n";
}
if (self::endsWith(self::$delimiter, $sql)) {
$statements[] = self::substring($sql, 0, strlen($sql) - 1 - strlen(self::$delimiter));
$sql = "";
}
}
return $statements;
return $statements;
}
//
// Some string helper methods
//
//
/**
* Tests if a string starts with a given string.
* @param string $check The substring to check.
* @param string $string The string to check in (haystack).
* @return boolean True if $string starts with $check, or they are equal, or $check is empty.
*/
protected static function startsWith($check, $string)
{
protected static function startsWith($check, $string) {
if ($check === "" || $check === $string) {
return true;
} else {
return (strpos($string, $check) === 0);
}
}
/**
* Tests if a string ends with a given string.
* @param string $check The substring to check.
* @param string $string The string to check in (haystack).
* @return boolean True if $string ends with $check, or they are equal, or $check is empty.
*/
protected static function endsWith(string $check, string $string)
{
protected static function endsWith($check/*: string*/, $string) {
if ($check === "" || $check === $string) {
return true;
} else {
return (strpos(strrev($string), strrev($check)) === 0);
}
}
}
/**
* a natural way of getting a subtring, php's circular string buffer and strange
* return values suck if you want to program strict as of C or friends
* @param string $string The string to get the substring from.
* @param int $startpos The start position of the substring.
* @param int $endpos The end position of the substring.
* @return string The substring.
* return values suck if you want to program strict as of C or friends
*/
protected static function substring(string $string, int $startpos, int $endpos = -1)
{
protected static function substring($string, $startpos, $endpos = -1) {
$len = strlen($string);
$endpos = (int) (($endpos === -1) ? $len - 1 : $endpos);
if ($startpos > $len - 1 || $startpos < 0) {
$endpos = (int) (($endpos === -1) ? $len-1 : $endpos);
if ($startpos > $len-1 || $startpos < 0) {
trigger_error("substring(), Startindex out of bounds must be 0<n<$len", E_USER_ERROR);
}
if ($endpos > $len - 1 || $endpos < $startpos) {
trigger_error("substring(), Endindex out of bounds must be $startpos<n<" . ($len - 1), E_USER_ERROR);
if ($endpos > $len-1 || $endpos < $startpos) {
trigger_error("substring(), Endindex out of bounds must be $startpos<n<".($len-1), E_USER_ERROR);
}
if ($startpos === $endpos) {
return (string) $string[$startpos];
} else {
$len = $endpos - $startpos;
$len = $endpos-$startpos;
}
return substr($string, $startpos, $len + 1);
return substr($string, $startpos, $len+1);
}
/**
* Convert string buffer into array of lines.
*
*
* @param string $buffer
* @return string[] lines of file.
* @return array string[] lines of file.
*/
protected static function getLines(string $buffer): array
{
$lines = preg_split("/\r?\n|\r/", $buffer);
return $lines === false ? [] : $lines;
protected static function getLines($buffer) {
$lines = preg_split("/\r?\n|\r/", $buffer);
return $lines;
}
}
}

View file

@ -2,16 +2,10 @@
namespace ctiso\Tools;
class StringUtil
{
class StringUtil {
/**
* Преобразует строку в массив
* @param string $str
* @return array
*/
static function strToArray(string $str): array
{
// from creole
static function strToArray($str) {
$str = substr($str, 1, -1); // remove { }
$res = [];
@ -19,7 +13,7 @@ class StringUtil
$in_subarr = 0;
$toks = explode(',', $str);
foreach ($toks as $tok) {
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
@ -27,7 +21,7 @@ class StringUtil
$in_subarr--;
}
} elseif ($tok[0] === '{') { // we're inside a new sub-array
if ('}' !== substr($tok, -1, 1)) {
if ('}' !== substr($tok, -1, 1)) {
$in_subarr++;
// if sub-array has more than one element
$subarr[$in_subarr] = [];
@ -38,131 +32,81 @@ class StringUtil
} else { // not sub-array
$val = trim($tok, '"'); // remove " (surrounding strings)
// perform type castng here?
$res[] = $val;
$res[] = $val;
}
}
return $res;
}
/**
* Нормализация строк на русском
* @param string $str
* @return string
*/
static function normalizeRussian(string $str): string
{
$result = preg_replace('/\s+/', ' ', $str);
//Нормализация строк на русском
static function normalizeRussian($str) {
$result = preg_replace('/\s+/',' ', $str);
if (is_string($result)) {
$result = trim($result); //Замена длинных пробелов на одинарные, пробелы по краям
$result = mb_strtolower($result);
$result = preg_replace('/ё/', 'е', $str); //е на ё
$result = preg_replace('/ё/','е', $str); //е на ё
}
return $result;
}
/**
* Проверка равенства двух строк на русском языке.
* @param string $str1
* @param string $str2
* @return bool
*/
static function equalRussianCheck($str1, $str2): bool
{
//Проверка равенства двух строк на русском языке.
static function equalRussianCheck($str1,$str2) {
return self::normalizeRussian($str1) == self::normalizeRussian($str2);
}
/**
* Попадает ли строка в список вариантов
* input: $str="foo1" $variants="foo1|foo2|foo3"
* output: true
* input: $str="foo" $variants="foo1|foo2|foo3"
* output: false
*
* @param string $str
* @param string $variants
* @return bool
*/
static function compare_string_to_variants($str, string $variants)
{
*/
static function compare_string_to_variants($str, $variants){
$variants_array = explode('|', $variants);
$founded = false;
foreach ($variants_array as $variant) {
$founded = $founded || self::equalRussianCheck($variant, $str);
$founded = $founded || self::equalRussianCheck($variant, $str);
}
return $founded;
}
/**
* Разбивает строку на массив символов
* @param string $str
* @return array
*/
static function mb_str_split(string $str): array
{
return preg_split('~~u', $str, -1, PREG_SPLIT_NO_EMPTY) ?: [];
static function mb_str_split($str) {
return preg_split('~~u', $str, -1, PREG_SPLIT_NO_EMPTY);
}
/**
* Заменяет символы в строке на символы из другой строки
* @param string $str
* @param string $from
* @param string $to
* @return string
*/
static function mb_strtr($str, $from, $to)
{
static function mb_strtr($str, $from, $to) {
return str_replace(self::mb_str_split($from), self::mb_str_split($to), $str);
}
static function encodestring(string $st): string
{
$st = self::mb_strtr($st, "абвгдеёзийклмнопрстуфхъыэ !+()", "abvgdeeziyklmnoprstufh_ie_____");
$st = self::mb_strtr($st, "АБВГДЕЁЗИЙКЛМНОПРСТУФХЪЫЭ", "ABVGDEEZIYKLMNOPRSTUFH_IE");
static function encodestring($st) {
$st = self::mb_strtr($st,"абвгдеёзийклмнопрстуфхъыэ !+()", "abvgdeeziyklmnoprstufh_ie_____");
$st = self::mb_strtr($st,"АБВГДЕЁЗИЙКЛМНОПРСТУФХЪЫЭ", "ABVGDEEZIYKLMNOPRSTUFH_IE");
$st = strtr($st, [
" " => '_',
"." => '_',
"," => '_',
"?" => '_',
"\"" => '_',
"'" => '_',
"/" => '_',
"\\" => '_',
"%" => '_',
"#" => '_',
"*" => '_',
"ж" => "zh",
"ц" => "ts",
"ч" => "ch",
"ш" => "sh",
"щ" => "shch",
"ь" => "",
"ю" => "yu",
"я" => "ya",
"Ж" => "ZH",
"Ц" => "TS",
"Ч" => "CH",
"Ш" => "SH",
"Щ" => "SHCH",
"Ь" => "",
"Ю" => "YU",
"Я" => "YA",
"Й" => "i",
"й" => "ie",
"ё" => "Ye",
"" => "N"
]);
" " => '_',
"." => '_',
"," => '_',
"?" => '_',
"\"" => '_',
"'" => '_',
"/" => '_',
"\\" => '_',
"%" => '_',
"#" => '_',
"*" => '_',
"ж"=>"zh", "ц"=>"ts", "ч"=>"ch", "ш"=>"sh",
"щ"=>"shch","ь"=>"", "ю"=>"yu", "я"=>"ya",
"Ж"=>"ZH", "Ц"=>"TS", "Ч"=>"CH", "Ш"=>"SH",
"Щ"=>"SHCH","Ь"=>"", "Ю"=>"YU", "Я"=>"YA",
"Й"=>"i", "й"=>"ie", "ё"=>"Ye",
""=>"N"
]);
return strtolower($st);
}
/**
* Проверяет, является ли строка кодированной
* @param string $st
* @return int|false
*/
static function validate_encoded_string(string $st): int|false
{
static function validate_encoded_string($st) {
$enc_st = self::encodestring($st);
return preg_match('/^[\w_-]+(\.[\w_-]+)?$/', $enc_st);
}
}
}

View file

@ -3,18 +3,13 @@
/**
* Формат для композиции изображений
*/
namespace ctiso\Tools;
use ctiso\Tools\Drawing;
use GdImage;
class TemplateImage
{
/** @var array<string, string> */
static array $listfiles = array('jpg' => 'jpeg', 'gif' => 'gif', 'png' => 'png', 'bmp' => 'wbmp');
/** @var array<string, string> */
static array $listfonts = array(
static $listfiles = array('jpg' => 'jpeg', 'gif' => 'gif', 'png' => 'png', 'bmp' => 'wbmp');
static $listfonts = array(
'georgia' => 'georgia.ttf',
'georgiabd' => 'georgiab.ttf',
'georgiaz' => 'georgiaz.ttf',
@ -29,28 +24,26 @@ class TemplateImage
'' => 'arial.ttf',
'dejavu' => 'DejaVuCondensedSerif.ttf',
'dejavubd' => 'DejaVuCondensedSerifBold.ttf',
'dejavuz' => 'DejaVuCondensedSerifBoldItalic.ttf',
'dejavuz' =>'DejaVuCondensedSerifBoldItalic.ttf',
'dejavui' => 'DejaVuCondensedSerifItalic.ttf',
'miriad' => 'MyriadPro-Cond.ttf',
'miriadbd' => 'MyriadPro-BoldCond.ttf'
);
/** @var string */
protected $src;
protected array $context = [];
protected array $data = [];
protected string $base = "c:\\windows\\fonts\\";
protected GdImage $image;
/** @var bool */
protected $context = array();
protected $data = array();
protected $base = "c:\\windows\\fonts\\";
protected $image;
protected $_prepare = true;
/** @var bool */
public $debug = false;
public string $resource;
public string $filename;
public $resource;
public $filename;
function __construct(?array $template = null)
function __construct ($template = null)
{
// assert(is_string($src));
if ($template) {
$this->data = $template;
}
@ -59,165 +52,131 @@ class TemplateImage
/**
* Путь к изображению
*/
function resourcePath(string $path): void
function resourcePath($path)
{
assert(is_string($path));
$this->resource = $path;
}
/**
* Путь у шрифтам
*/
function fontPath(string $path): void
function fontPath($path)
{
assert(is_string($path));
$this->base = $path;
}
/**
* @param string $name
* @param mixed $value
*/
function set(string $name, $value): void
function set($name, $value)
{
$this->context['[' . $name . ']'] = $value;
assert(is_string($name));
$this->context['['.$name.']'] = $this->encode($value);
}
function setImage(string $name): void
function setImage($name)
{
$this->filename = $name;
$this->image = $this->imagefromfile($name);
}
/**
* Создает пустое изображение
* @param int<1, max> $width
* @param int<1, max> $height
*/
function setEmptyImage($width, $height): void
function setEmptyImage($width, $height)
{
$this->image = imagecreatetruecolor($width, $height);
}
/**
* Создает изображение из файла
* @param string $file
* @return GdImage|null
*/
function imagefromfile(string $file)
function imagefromfile($file)
{
assert(is_string($file));
$suffix = pathinfo($file, PATHINFO_EXTENSION);
if (array_key_exists($suffix, self::$listfiles)) {
$imageFn = 'imagecreatefrom' . self::$listfiles[$suffix];
if (!is_callable($imageFn)) {
return null;
}
return call_user_func($imageFn, $file);
return call_user_func('imagecreatefrom' . self::$listfiles[$suffix], $file);
}
return null;
}
function getFontFile(string $name): string
function getFontFile($name)
{
if (array_key_exists(strtolower($name), self::$listfonts)) {
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(array $style): string
function fontSuffix($style)
{
if ($style[0] && $style[1]) return "z";
if($style[0] && $style[1]) return "z";
if ($style[0]) return "bd";
if ($style[1]) return "i";
if($style[0]) return "bd";
if($style[1]) return "i";
return "";
}
/**
* @param string $text
* @param object{
* fontFamily: string,
* fontSize: int,
* fontStyle: array{string, string},
* color: string,
* align: array,
* valign: array,
* left: int,
* top: int,
* width: int,
* height: int
* } $value
*/
function imageText(string $text, object $value): void
function imageText($text, $value/*: \stdClass*/)
{
assert(is_string($text));
$text = strtr($text, $this->context);
$size = $value->fontSize;
$fontfile = $this->getFontFile($value->fontFamily . $this->fontSuffix($value->fontStyle));
$color = intval(substr($value->color, 1), 16);
if ($value->align[0]) {
if ($value->align[0]) {
$align = Drawing::ALIGN_LEFT;
} elseif ($value->align[2]) {
} elseif ($value->align[2]) {
$align = Drawing::ALIGN_RIGHT;
} else {
$align = Drawing::ALIGN_CENTER;
}
if ($value->valign[0]) {
if ($value->valign[0]) {
$valign = Drawing::ALIGN_TOP;
} elseif ($value->valign[1]) {
} 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
);
Drawing::imagettftextbox($this->image, $size, 0, $value->left, $value->top, $color, $fontfile, $text,
$value->width, $value->height,
$align, $valign);
}
/**
* Перекодировка текста
* @deprecated Можно заменить encode($x) -> $x
*/
function encode(string $text): string
function encode($text)
{
return $text;
assert(is_string($text));
return $text; //iconv("WINDOWS-1251", "UTF-8", $text);
}
/**
* @param int<1,max> $new_width
* @param ?int<1,max> $new_height
*/
function setSize(int $new_width, ?int $new_height = null): void
function setSize($new_width, $new_height)
{
$width = imagesx($this->image);
$height = imagesy($this->image);
if ($new_height == null) {
$new_height = max(1, (int)ceil($height * $new_width / $width));
$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);
// imagecopyresized($image_p, $this->image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
$this->image = $image_p;
}
function prepare(): void
{
if ($this->_prepare) {
function prepare() {
if($this->_prepare) {
$this->_prepare = false;
foreach ($this->data as $value) {
$this->imageText($value->text, $value); // break;
@ -228,8 +187,10 @@ class TemplateImage
/**
* Генерирует изображение из шаблона
*/
function render(?string $file = null): string|bool
function render($file = null)
{
assert(is_string($file) || is_null($file));
$this->prepare();
if ($file == null) {

View file

@ -2,20 +2,13 @@
namespace ctiso;
class UTF8
{
/**
* @param string $str
* @param int $split_length
* @return list<string>
*/
static function str_split(string $str, int $split_length = 1): array
{
$split_length = (int) $split_length;
class UTF8 {
static function str_split($str, $split_length = 1) {
$split_length = (int) $split_length;
$matches = [];
preg_match_all('/.{' . $split_length . '}|[^\x00]{1,' . $split_length . '}$/us', $str, $matches);
return $matches[0];
preg_match_all('/.{'.$split_length.'}|[^\x00]{1,'.$split_length.'}$/us', $str, $matches);
return $matches[0];
}
}
}

View file

@ -3,29 +3,25 @@
namespace ctiso;
class Url {
/** @var array<string, string> */
public array $parts = [];
public ?Url $parent;
public $parts = [];
public $parent/*: Url*/;
/**
* @param Url|null $parent
*/
function setParent($parent): void {
function __construct() {
}
function setParent($parent) {
$this->parent = $parent;
}
/**
* @param string[] $parts
*/
function setQuery(array $parts): void {
function setQuery($parts) {
$this->parts = $parts;
}
function addQueryParam(string $key, ?string $value): void {
function addQueryParam($key, $value) {
$this->parts[$key] = $value;
}
function toString(): string {
function toString() {
return '?' . http_build_query(array_merge($this->parts, $this->parent ? $this->parent->parts : []));
}
}
}

View file

@ -6,11 +6,7 @@
namespace ctiso;
class UserMessageException extends \Exception {
/** @var string */
public $userMessage;
/**
* @param string $message
*/
public function __construct($message) {
parent::__construct($message);
$this->userMessage = $message;

View file

@ -1,57 +1,47 @@
<?php
namespace ctiso\Validator\Rule;
use ctiso\Collection;
abstract class AbstractRule
{
public string $field;
protected ?string $errorMsg;
/** @var RuleContext */
public $field;
protected $errorMsg;
protected $ctx;
public function __construct(string $field, ?string $errorMsg = null)
public function __construct($field, $errorMsg = null)
{
$this->field = $field;
$this->errorMsg = $errorMsg;
}
public function setName(string $field): self
public function setName($field)
{
$this->field = $field;
return $this;
}
public function setErrorMsg(?string $errorMsg): self
public function setErrorMsg($errorMsg)
{
$this->errorMsg = $errorMsg;
return $this;
}
public function getErrorMsg(): string
{
public function getErrorMsg()
{
return $this->errorMsg;
}
/**
* @param Collection $container
* @param bool|null $status
* @return bool
*/
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
return true;
}
function skipEmpty(): bool {
function skipEmpty() {
return true;
}
/**
* @param RuleContext $ctx
*/
public function setContext($ctx): void
public function setContext($ctx)
{
$this->ctx = $ctx;
}

View file

@ -4,18 +4,17 @@
* Проверка на число
*/
namespace ctiso\Validator\Rule;
use ctiso\Validator\Rule\AbstractRule;
use ctiso\Collection;
use ctiso\Validator\Rule\AbstractRule,
ctiso\Collection;
class Alpha extends AbstractRule
{
public function getErrorMsg(): string
{
public function getErrorMsg()
{
return "Поле должно содержать только буквы";
}
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
return ctype_alpha($container->get($this->field));
}

View file

@ -13,7 +13,7 @@ class Code extends AbstractRule
return "Неправильно указан персональный код";
}
function checkCode(array $code): bool {
function checkCode($code): bool {
foreach($code as $c) {
if (empty($c)) {
return false;

View file

@ -9,30 +9,26 @@ use ctiso\Validator\Rule\AbstractRule,
class Count extends AbstractRule
{
public int $size = 1;
public ?int $max = null;
public $size = 1;
public $max = null;
public function getErrorMsg(): string
{
public function getErrorMsg()
{
return "Количество записей должно быть не менне {$this->size} и не более {$this->max}";
}
/**
* @param string $s
* @return bool
*/
function notEmpty($s): bool {
function not_empty($s) {
return $s != "";
}
public function isValid(Collection $container, $status = null): bool
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))), [$this, 'notEmpty']));
}
$count = count(array_filter(array_map('trim',
explode(";", $container->get($this->field))), [$this, 'not_empty']));
return $count >= $this->size && $count <= ((int)$this->max);
}

View file

@ -9,16 +9,16 @@ use ctiso\Validator\Rule\AbstractRule,
class Date extends AbstractRule
{
public function getErrorMsg(): string
{
public function getErrorMsg()
{
return "Неверный формат даты";
}
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
$pattern = "/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})$/";
$matches = [];
return (preg_match($pattern, $container->get($this->field), $matches)
return (preg_match($pattern, $container->get($this->field), $matches)
&& checkdate((int)$matches[2], (int)$matches[1], (int)$matches[3]));
}
}

View file

@ -9,12 +9,12 @@ use ctiso\Validator\Rule\AbstractRule,
class Email extends AbstractRule
{
public function getErrorMsg(): string
public function getErrorMsg()
{
return "Неверный формат электронной почты";
}
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
$emails = explode(",", $container->get($this->field));
foreach ($emails as $email) {

View file

@ -9,14 +9,20 @@ use ctiso\Validator\Rule\AbstractRule,
class EmailList extends AbstractRule
{
public function getErrorMsg(): string
{
public function getErrorMsg()
{
return "Неверный формат электронной почты";
}
public function isValid(Collection $container, $status = null): bool {
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

@ -7,12 +7,12 @@ use ctiso\Validator\Rule\AbstractRule,
class FileName extends AbstractRule {
public function getErrorMsg(): string
public function getErrorMsg()
{
return 'Название файла может содержать только символы латиницы в нижнем регистре и цифры';
}
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
return Path::isName($container->get($this->field));
}

View file

@ -9,42 +9,42 @@ use ctiso\Validator\Rule\AbstractRule,
class IsFile extends AbstractRule
{
private array $type = [];
private int $maxsize = 1024;
private $type = array();
private $maxsize = 1024;
function skipEmpty(): bool {
function skipEmpty() {
return false;
}
function setSize(int $size): void {
function setSize($size) {
$this->maxsize = $size;
}
function setType(array $type): void {
function setType(array $type) {
$this->type = $type;
}
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
if (!isset($_FILES[$this->field]) || $_FILES[$this->field]['error'] == UPLOAD_ERR_NO_FILE) {
$this->setErrorMsg('Файл не загружен');
return false;
return false;
}
if ($_FILES[$this->field]['error'] == UPLOAD_ERR_INI_SIZE) {
$this->setErrorMsg('Превышен размер файла');
return false;
return false;
}
$tmp = $_FILES[$this->field];
if (!in_array($tmp['type'], $this->type)) {
$this->setErrorMsg('Неверный формат файла');
return false;
return false;
}
if (((int)$tmp['size']) > $this->maxsize*1024) {
$this->setErrorMsg('Неверный размер файла');
return false;
return false;
}
return true;

View file

@ -9,16 +9,14 @@ use ctiso\Validator\Rule\AbstractRule,
class MatchRule extends AbstractRule
{
/** @var string */
public $same;
public function getErrorMsg(): string
{
public function getErrorMsg()
{
return "Поля не совпадают";
}
public function isValid(Collection $container, $status = null): bool
{
public function isValid(Collection $container, $status = null) {
return (strcmp($container->get($this->field), $container->get($this->same)) == 0);
}
}

View file

@ -6,16 +6,16 @@ use ctiso\Validator\Rule\AbstractRule,
class Notnull extends AbstractRule
{
function skipEmpty(): bool {
function skipEmpty() {
return false;
}
public function getErrorMsg(): string
{
public function getErrorMsg()
{
return "Поле не должно быть пустым";
}
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
$data = $container->get($this->field);
if (is_array($data)) {

View file

@ -9,12 +9,12 @@ use ctiso\Validator\Rule\AbstractRule,
class Numeric extends AbstractRule
{
public function getErrorMsg(): string
{
public function getErrorMsg()
{
return "Значение поля должно быть числом";
}
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
return (is_numeric($container->get($this->field)));
}

View file

@ -1,20 +1,21 @@
<?php
/**
*/
namespace ctiso\Validator\Rule;
use ctiso\Validator\Rule\AbstractRule,
ctiso\Collection;
class PregMatch extends AbstractRule
{
public string $pattern;
public function getErrorMsg(): string
{
return "Поле в неправильном формате";
}
public $pattern;
public function getErrorMsg()
{
return "Поле в неправильном формате";
}
public function isValid(Collection $container, $status = null): bool
{
return preg_match($this->pattern, $container->get($this->field)) !== false;
}
public function isValid(Collection $container, $status = null)
{
return preg_match($this->pattern,$container->get($this->field));
}
}

View file

@ -1,10 +0,0 @@
<?php
namespace ctiso\Validator\Rule;
use ctiso\Collection;
use ctiso\HttpRequest;
interface RuleContext {
function getMessage(): string;
function isUnique(string $field, mixed $status, Collection $collection): bool;
}

View file

@ -9,23 +9,21 @@ use ctiso\Validator\Rule\AbstractRule,
class Time extends AbstractRule
{
/** @var non-empty-string */
private string $split = ":";
private $split = ":";
public function getErrorMsg(): string
public function getErrorMsg()
{
return "Неверный формат времени";
}
static function checktime(int $hour, int $minute): bool
static function checktime($hour, $minute)
{
if ($hour > -1 && $hour < 24 && $minute > -1 && $minute < 60) {
return true;
}
return false;
}
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
/** @var list<string> */
$tmp = explode($this->split, $container->get($this->field), 2);

View file

@ -3,20 +3,19 @@
/**
*/
namespace ctiso\Validator\Rule;
use ctiso\Validator\Rule\AbstractRule;
use ctiso\Collection;
use ctiso\Validator\Rule\AbstractRule,
ctiso\Collection;
class Unique extends AbstractRule
{
public function getErrorMsg(): string
public function getErrorMsg()
{
return $this->ctx->getMessage();
}
public function isValid(Collection $container, $status = null): bool
public function isValid(Collection $container, $status = null)
{
return $this->ctx->isUnique($container->getString($this->field), $status, $container);
return $this->ctx->isUnique($container->get($this->field), $status, $container);
}
}

View file

@ -1,31 +1,23 @@
<?php
///<reference path="Rule/Notnull.php"/>
/**
* Проверка коллекции
*/
namespace ctiso\Validator;
use Exception;
use ctiso\Validator\Rule\AbstractRule;
use ctiso\Validator\Rule\RuleContext;
use ctiso\Collection;
use Exception,
ctiso\Validator\Rule\AbstractRule,
ctiso\Collection;
/**
* @phpstan-type Rule array{
* validate?:string, // Описание правила см. формат правила ниже
* name:string, // Имя переменой для проверки
* context?:RuleContext
* }
*/
class Validator
{
/** @var AbstractRule[] */
protected array $chain = []; // Массив правил
/** @var array<string, string> */
protected array $errorMsg = []; // Массив ошибок
protected $chain = []; // Массив правил
protected $errorMsg = []; // Массив ошибок
/**
* Поля по умолчанию
* @var array<string, class-string<AbstractRule>>
* @var array<string, class-string<Rule\AbstractRule>>
*/
protected $type = [
'date' => Rule\Date::class,
@ -44,27 +36,21 @@ class Validator
'reg' => Rule\PregMatch::class,
];
/**
* @param Rule[] $rules
*/
function __construct($rules = []) {
$this->addRuleList($rules);
}
/**
* Добавление правила в список
* @param string $name
* @param class-string<AbstractRule> $className
*/
function addRuleType(string $name, string $className): void {
function addRuleType($name, $className) {
$this->type[$name] = $className;
}
/**
* Добавление списка правил в специальном формате
* @param Rule[] $input
* array(array('name' => fieldname, 'validate' => ruletext), ...)
* fieldname - Имя переменой для проверки
* ruletext - Описание правила см. формат правила ниже
*/
public function addRuleList(array $input): void
public function addRuleList(array $input)
{
// Разбор правила проверки
// Формат правила 'rule1|rule2,param1=value1|rule3,param1=value1,param2=value2'
@ -97,7 +83,7 @@ class Validator
}
}
public function addRule(array|AbstractRule $rule): void {
public function addRule($rule/*:z any*/) {
if (is_array($rule)) {
$this->chain = array_merge($this->chain, $rule);
} else {
@ -105,10 +91,7 @@ class Validator
}
}
/**
* @param AbstractRule $rule
*/
public function skip($rule, Collection $container): bool
public function skip($rule/*z: AbstractRule*/, $container/*: Collection*/) // -> Rule_Abstract
{
if ($rule->skipEmpty()) {
$data = $container->get($rule->field);
@ -120,23 +103,17 @@ class Validator
return false;
}
function reset(): void {
function reset() {
$this->errorMsg = [];
}
/**
* @param Collection $container
* @param AbstractRule[]|null $rules
* @param bool|null $status
* @return bool
*/
public function validate(Collection $container, $rules = null, $status = null): bool
public function validate(Collection $container, $rule = null, $status = null)
{
$fields = [];
if ($rules) {
$this->chain = $rules;
if ($rule) {
$this->chain = $rule;
}
// $this->errorMsg = [];
foreach ($this->chain as $rule) {
//echo $key;
if (!in_array($rule->field, $fields) && !$this->skip($rule, $container) && !$rule->isValid($container, $status)) {
@ -148,25 +125,22 @@ class Validator
return $this->isValid();
}
public function addError(string $name, string $message): void
public function addError($name, $message)
{
$this->errorMsg[$name] = $message;
}
public function isError(): bool
public function isError()
{
return !empty($this->errorMsg);
}
public function isValid(): bool
public function isValid()
{
return empty($this->errorMsg);
}
/**
* @return array<string, string>
*/
public function getErrorMsg(): array
public function getErrorMsg()
{
return $this->errorMsg;
}

View file

@ -1,46 +1,45 @@
<?php
namespace ctiso\View;
use ctiso\View\View;
use PHPTAL;
use PHPTAL_TranslationService;
use ctiso\View\View,
PHPTAL;
class Composite extends View
{
private PHPTAL $tal;
private $tal;
public $config;
function __construct(string $file)
function __construct($file)
{
parent::__construct();
$this->tal = new PHPTAL($file);
$this->tal->setPhpCodeDestination(PHPTAL_PHP_CODE_DESTINATION);
$this->tal->setEncoding(PHPTAL_DEFAULT_ENCODING);
$this->tal->setEncoding(PHPTAL_DEFAULT_ENCODING);
$this->tal->setTemplateRepository(PHPTAL_TEMPLATE_REPOSITORY);
$this->tal->setOutputMode(PHPTAL::HTML5);
$this->tal->stripComments(true);
// $this->tal->addPreFilter(new PHPTAL_PreFilter_Normalize());
}
function set(string $key, mixed $val): void {
function set($key, $val) {
if ($key == 'title') {
$this->setTitle($val);
}
$this->tal->set($key, $val);
}
function __set(string $key, mixed $val): void {
function __set($key, $val) {
$this->tal->set($key, $val);
}
function execute(): string
function execute()
{
$this->processChild();
return $this->tal->execute();
}
function setTranslator(PHPTAL_TranslationService $t): void {
function setTranslator($t) {
$this->tal->setTranslator($t);
}
}

View file

@ -1,40 +0,0 @@
<?php
namespace ctiso\View;
class JsonView extends \stdClass {
/** @var array */
public $_data = [];
/** @var string */
public $_name = '';
/**
* @param string $name
*/
function __construct($name) {
$this->_name = $name;
}
/**
* @param string $key
* @param mixed $value
*/
function set($key, $value): void {
$this->_data[$key] = $value;
}
/**
* @param string $key
* @param mixed $value
*/
function __set($key, $value): void {
$this->_data[$key] = $value;
}
/**
* @return string
*/
function execute() {
return json_encode($this->_data) ?: '';
}
}

View file

@ -8,14 +8,6 @@ namespace ctiso\View;
class Pages
{
static int $range = 5;
/**
* @param int $page номер страницы
* @param int $onpage количество страниц на странице
* @param int $count количество всех страниц
* @param string $prefix префикс
* @return array{'all': bool, 'list': array, 'first': string, 'last': string, 'next': string|false, 'prev': string|false}
*/
static function getPages($page, $onpage, $count, $prefix = '?')
{
$n = ceil($count / $onpage);
@ -28,30 +20,30 @@ class Pages
}
return [
'all' => ($n > 1),
'list' => $result,
'first' => self::href($prefix, $url . 1),
'last' => self::href($prefix, $url . $n),
'next' => ($page == $n)? false : self::href($prefix, $url . ($page + 1)) ,
'list' => $result,
'first' => self::href($prefix, $url . 1),
'last' => self::href($prefix, $url . $n),
'next' => ($page == $n)? false : self::href($prefix, $url . ($page + 1)) ,
'prev' => ($page == 1)? false : self::href($prefix, $url . ($page - 1))];
}
/**
* @deprecated
* @param int $page номер страницы
* @param int $onpage количество элем на странице
* @param $page int номер страницы
* @param $onpage int количество элем на странице
* @return string
*/
static function getLimit(int $page, int $onpage) {
static function getLimit($page/*: number*/, $onpage/*: number*/) {
if ($page <= 0) { $page = 1; }
return "LIMIT $onpage OFFSET " . ($page - 1) * $onpage;
}
/**
* @param int $page номер страницы
* @param int $onpage количество элем на странице
* @param $page int номер страницы
* @param $onpage int количество элем на странице
* @return array
*/
static function _getLimit(int $page, int $onpage) {
static function _getLimit($page, $onpage) {
if ($page <= 0) { $page = 1; }
return [
'count' => $onpage,
@ -59,13 +51,8 @@ class Pages
];
}
/**
* @param string $prefix префикс
* @param string $x строка
* @return string
*/
static function href($prefix, $x) {
return $prefix . $x;
static function href($prefix, $x) {
return $prefix . $x;
}
}

View file

@ -7,70 +7,41 @@ namespace ctiso\View;
*/
class Plain extends \stdClass
{
/** @var string */
protected $document;
/** @var array */
protected $values = [];
protected $values = array();
/**
* Конструктор
* @param string $document шаблон
*/
public function __construct ($document)
{
$this->document = $document;
}
/**
* Установка значения
* @param string $key ключ
* @param mixed $value значение
*/
public function set($key, $value): void
public function set($key, $value)
{
$this->values[$key] = $value;
}
/**
* Импорт значений
* @param array $list список значений
*/
public function import($list): void
public function import($list)
{
$this->values = array_merge($this->values, $list);
}
/**
* @param string $key ключ
* @param mixed $value значение
*/
public function __set($key, $value): void
public function __set($key, $value)
{
$this->set($key, $value);
}
/**
* Выполнение шаблона
* @return string
*/
public function execute()
{
$result = $this->values;
return self::getTemplateContent ($this->document, $result);
}
/**
* Получение содержимого шаблона
* @param string $document шаблон
* @param array $result результат
* @return string содержимое шаблона
*/
static function getTemplateContent(string $document, $result): string
static function getTemplateContent($document, $result)
{
ob_start ();
include ($document);
include ($document);
$content = ob_get_contents ();
ob_clean ();
return $content === false ? '' : $content;
return $content;
}
}

View file

@ -8,32 +8,16 @@ class Top extends Composite
{
/**
* Общая строка заголовка
* @var int
*/
public $mid = 0;
/** @var array */
public $require = [];
/** @var array */
public $deps = [];
/** @var \ctiso\Registry */
public $config;
public $require = array();
public $deps = array();
/**
* Заголовок страницы
*
* @return string
*/
public function getTitle()
{
return implode(" - ", array_filter($this->doTree('_title', false), [$this, 'isNotNull']));
}
/**
* Идентификатор
*
* @param string $pref
* @return string
*/
function getId($pref)
{
$this->mid++;
@ -45,8 +29,7 @@ class Top extends Composite
*
* @return string
*/
#[\Override]
public function execute(): string
public function render()
{
$this->doTree('alias');
@ -85,12 +68,11 @@ class Top extends Composite
}
$init = [];
/** @var View $item */
foreach ($s->_section as $key => $item) {
if ($item->codeGenerator !== null) {
$ss /*: View*/= $item;
if ($ss->codeGenerator !== null) {
// функцию которая вычисляет а не результат
$part = call_user_func($item->codeGenerator, $this, $key, $value);
$part = call_user_func($ss->codeGenerator, $this, $key, $value);
$init[] = $part;
}
}
@ -107,14 +89,14 @@ class Top extends Composite
$this->set('title', $this->getTitle());
$this->set('jspath', $this->config->get('system', 'web'));
//
return parent::execute(); // execute+phptal ??
//
return $this->execute(); // execute+phptal ??
}
/**
* Массив имен файлов скриптов
*
* @return array
* return array
*/
public function getScripts()
{
@ -131,11 +113,6 @@ class Top extends Composite
return implode("\n", $this->doTree('_scriptstring'));
}
/**
* Строка со скриптом
*
* @return string
*/
public function getScriptStartup()
{
return implode("\n", $this->doTree('_startup'));
@ -144,10 +121,11 @@ class Top extends Composite
/**
* Массив имен файлов стилей
*
* @return array
* return array
*/
public function getStyleSheet()
{
return $this->doTree('_stylesheet');
}
}

View file

@ -5,35 +5,24 @@ use Exception;
class View extends \stdClass
{
/** @var array<View|string> Вложенные шаблоны */
protected array $_section = [];
protected $_section = array(); // Вложенные шаблоны
// Блоки
protected $_stylesheet = array(); // Массив стилей текущего шаблона
protected $_script = array(); // Массив скриптов текущего шаблона
public $_scriptstring = array();
protected $_startup = array();
protected $_values = array();
/** @var string[] $_stylesheet Массив стилей текущего шаблона */
protected array $_stylesheet = [];
/** @var string[] $_script Массив скриптов текущего шаблона */
protected array $_script = [];
/** @var string[] $_scriptstring */
public array $_scriptstring = [];
/** @var string[] $_startup */
protected array $_startup = [];
protected $_title = null; // Заголовок текущего шаблона
protected array $_values = [];
public $active_module;
public $module_action;
public $prefix;
protected ?string $_title = null; // Заголовок текущего шаблона
public $suggestions; //подсказки
public ?string $active_module = null;
public string $module_action;
/** @var string[] */
public array $prefix;
/** @var string[] */
public array $suggestions = []; //подсказки
/** @var array<string, string|array<string, string>> */
public array $alias = [];
// public $codeGenerator = null;
/** @var View|null */
public $alias = [];
public $codeGenerator = null;
public $parent_view = null;
function __construct() {
@ -45,17 +34,14 @@ class View extends \stdClass
* @param string $section переменная шаблона
* @param View|string $view вложенный шаблон
*/
public function setView($section, $view): void
public function setView($section, $view)
{
$this->_section[$section] = $view;
$this->_section [$section] = $view;
if (is_object($view)) {
$view->parent_view = $this;
}
}
/**
* @param array $values
*/
public function assignValues($values): void
{
$this->_values = $values;
@ -67,7 +53,7 @@ class View extends \stdClass
*
* @param string $name путь к скрипту
*/
public function addScript(string $name): void
public function addScript($name): void
{
$output = $this->resolveName($this->alias, $name . '?' . http_build_query($this->prefix));
$this->_script [] = $output;
@ -76,18 +62,18 @@ class View extends \stdClass
/**
* Добавляет код скипта к текущему шаблону
*
* @param string $code строка javascript кода
* @param string $name строка javascript кода
*/
public function addScriptRaw(string $code, bool $startup = false): void
public function addScriptRaw($name, $startup = false): void
{
if ($startup) {
$this->_startup [] = $code;
$this->_startup [] = $name;
} else {
$this->_scriptstring [] = $code;
$this->_scriptstring [] = $name;
}
}
public function setPrefix(string $name, string $val): void {
public function setPrefix($name, $val) {
$this->prefix[$name] = $val;
}
@ -96,7 +82,7 @@ class View extends \stdClass
*
* @param string $name путь к стилю
*/
public function addStyleSheet(string $name): void
public function addStyleSheet($name)
{
$output = $this->resolveName($this->alias, $name . '?' . http_build_query($this->prefix));
$this->_stylesheet [] = $output;
@ -106,9 +92,9 @@ class View extends \stdClass
* Рекурсивно извлекает из значение свойства обьекта
*
* @param string $list Имя свойства
* @param bool $flatten
* @param boolean $flatten
*/
protected function doTree($list, bool $flatten = true): array
protected function doTree($list, $flatten = true)
{
$result = ($flatten == true) ? $this->$list : [$this->$list];
foreach ($this->_section as $value) {
@ -123,15 +109,7 @@ class View extends \stdClass
return $result;
}
/*
function getTitleArray(): array {
return array_reduce($this->_section, fn ($result, $item) =>
is_object($item) ? array_merge($result, $item->getTitleArray()) : $result, []);
}
*/
/*abstract*/ public function set(string $key, mixed $value): void
/*abstract*/ public function set($key, $value)
{
}
@ -147,35 +125,31 @@ class View extends \stdClass
/**
* Установка заголовка шаблона
*
* @param string $title
*/
public function setTitle(string $title): void
public function setTitle($title): void
{
$this->_title = $title;
}
protected function isNotNull(?string $title): bool
protected function isNotNull($title): bool
{
return $title !== null;
}
/**
* @param array<string, string|array<string, string>> $alias
*/
function setAlias($alias): void
{
$this->alias = $alias;
}
function addAlias(string $name, string $path): void
function addAlias($name, $path): void
{
$this->alias[$name] = $path;
$this->set($name, $path);
}
/**
* @param string[] $pathlist
*/
function findFile(array $pathlist, string $file): string {
function findFile($pathlist, string $file): string {
foreach($pathlist as $key => $www) {
if (file_exists($key . '/' . $file)) {
@ -185,12 +159,8 @@ class View extends \stdClass
throw new Exception("file not found: $file");
}
/**
* FIXME: Префикс, конфликтует с протоколом
* @param string[]|string[][] $alias
*/
function resolveName($alias, string $file): string {
// FIXME: Префикс, конфликтует с протоколом
function resolveName($alias, $file) {
list($type, $filename) = explode(":", $file, 2);
// Сделать поиск а не просто замену папки при совпадении имени
@ -205,23 +175,11 @@ class View extends \stdClass
return $file;
}
/**
* @param string[][] $alias
* @param string[] $list
* @return string[]
*/
public function resolveAllNames($alias, array $list): array {
public function resolveAllNames($alias, $list) {
$result = [];
foreach($list as $item) {
$result [] = $this->resolveName($alias, $item);
}
return $result;
}
/**
* Шаблон всегда возвращает строку
*/
function execute(): string {
return '';
}
}

View file

@ -26,15 +26,12 @@ class ZipFile extends ZipArchive
// Read all Files in Dir
$dir = opendir($location);
if (!$dir) {
throw new \RuntimeException("Enable to open dir '$dir'");
}
while (($file = readdir($dir)) !== false)
{
if (in_array($file, $this->ignore)) continue;
// Rekursiv, If dir: FlxZipArchive::addDir(), else ::File();
$call = (is_dir($location . $file)) ? $this->addDir(...) : $this->addFile(...);
call_user_func($call, $location . $file, $name . $file);
$call = (is_dir($location . $file)) ? 'addDir' : 'addFile';
call_user_func([$this, $call], $location . $file, $name . $file);
}
}