Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a9ef3534b |
120 changed files with 3322 additions and 5423 deletions
|
|
@ -8,11 +8,8 @@
|
|||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ctiso\\": "src/"
|
||||
"psr-0": {
|
||||
"": "src/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"phpmailer/phpmailer": "^6.8.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
<?php
|
||||
define('PHPTAL_PHP_CODE_DESTINATION', '');
|
||||
define('PHPTAL_TEMPLATE_REPOSITORY', '');
|
||||
define('PHPTAL_DEFAULT_ENCODING', '');
|
||||
10
phpstan.neon
10
phpstan.neon
|
|
@ -1,10 +0,0 @@
|
|||
parameters:
|
||||
level: 3
|
||||
editorUrl: 'vscode://file/%%file%%:%%line%%'
|
||||
paths:
|
||||
- src
|
||||
universalObjectCratesClasses:
|
||||
- ctiso\View\Composite
|
||||
bootstrapFiles:
|
||||
- config.php
|
||||
|
||||
|
|
@ -1,28 +1,16 @@
|
|||
<?php
|
||||
|
||||
|
||||
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)) {
|
||||
|
|
|
|||
11
src/Arr.php
11
src/Arr.php
|
|
@ -1,16 +1,7 @@
|
|||
<?php
|
||||
|
||||
|
||||
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;
|
||||
return isset($data[$key]) ? $data[$key] : $default;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso;
|
||||
/**
|
||||
* Коллекция
|
||||
* @implements \ArrayAccess<string,mixed>
|
||||
*
|
||||
*/
|
||||
class Collection implements \ArrayAccess
|
||||
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 +25,6 @@ class Collection implements \ArrayAccess
|
|||
|
||||
/**
|
||||
* Преобразование коллекции в массив
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function export()
|
||||
{
|
||||
|
|
@ -33,13 +32,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 +48,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,114 +56,44 @@ 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);
|
||||
return (($result > 0) ? $result : $default);
|
||||
}
|
||||
|
||||
public function clear(): void
|
||||
public function clear()
|
||||
{
|
||||
$this->data = [];
|
||||
$this->data = array();
|
||||
}
|
||||
|
||||
public function offsetSet($key, $value): void
|
||||
public function offsetSet($key, $value)
|
||||
{
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
public function offsetExists($key): bool
|
||||
public function offsetExists($key)
|
||||
{
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
|
||||
public function offsetUnset($key): void
|
||||
public function offsetUnset($key)
|
||||
{
|
||||
unset($this->data[$key]);
|
||||
}
|
||||
|
||||
public function offsetGet($key): mixed
|
||||
public function offsetGet($key)
|
||||
{
|
||||
return $this->data[$key] ?? null;
|
||||
return isset($this->data[$key]) ? $this->data[$key] : null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso;
|
||||
|
||||
use ctiso\HttpRequest;
|
||||
use 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);
|
||||
}
|
||||
$arr = $this->r->get($key);
|
||||
if (!is_null($arr)) {
|
||||
if (is_array($arr)) {
|
||||
return Arr::get($arr, $this->component_id, $default);
|
||||
} else {
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function getAction() {
|
||||
return $this->r->getAction();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Connection;
|
||||
use ctiso\File;
|
||||
|
||||
class HttpRequest
|
||||
class Connection_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 +33,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 +86,7 @@ class HttpRequest
|
|||
$header = $this->getHeader();
|
||||
fwrite($socket, $header);
|
||||
|
||||
$result = '';
|
||||
$result = null;
|
||||
while (! feof($socket)) {
|
||||
$result .= fgets($socket, 128);
|
||||
}
|
||||
|
|
@ -120,12 +96,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;
|
||||
|
|
|
|||
|
|
@ -3,43 +3,32 @@
|
|||
/**
|
||||
* Обрабатывает HTTP ответ
|
||||
*/
|
||||
namespace ctiso\Connection;
|
||||
|
||||
class HttpResponse
|
||||
class Connection_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 +36,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());
|
||||
$chunk = [];
|
||||
while ($index > 0) {
|
||||
$chunk [] = substr($this->response, $this->offset, $index);
|
||||
$this->offset += $index;
|
||||
$index = (int)hexdec($this->getLine());
|
||||
$nline = hexdec($this->getLine());
|
||||
$chunk = array();
|
||||
while ($nline > 0) {
|
||||
$chunk [] = substr($this->response, $this->offset, $nline);
|
||||
$this->offset += $nline;
|
||||
$nline = hexdec($this->getLine());
|
||||
}
|
||||
|
||||
$this->data = implode("", $chunk);
|
||||
|
|
@ -64,33 +53,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,139 +1,112 @@
|
|||
<?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;
|
||||
function forceUrl($name)
|
||||
{
|
||||
if (is_callable($name)) {
|
||||
return call_user_func($name);
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Контроллер страниц
|
||||
*/
|
||||
class Action implements ActionInterface
|
||||
class Controller_Action
|
||||
{
|
||||
|
||||
const TEMPLATE_EXTENSION = ".html"; // Расширение для шаблонов
|
||||
const ACTION_PREFIX = "action"; // Префикс для функций действий
|
||||
|
||||
// Параметры устанавливаются при создании контроллера
|
||||
public string $name = ''; // Имя модуля
|
||||
/** @var \ctiso\Controller\Front */
|
||||
public $front;
|
||||
public $jsPath; // Глобальный путь к скриптам
|
||||
public $themePath; // Глобальный путь к текущей теме
|
||||
|
||||
public string $modulePath = ''; // Путь к модулю
|
||||
public string $moduleTitle = '';
|
||||
// Параметры устанавливаются при создании контроллера
|
||||
public $name = ''; // Имя модуля
|
||||
public $viewPath = null; // Путь к шаблонам контроллера
|
||||
public $viewPathPrefix = null; // Путь к шаблонам контроллера
|
||||
|
||||
public string $viewPathPrefix = ''; // Путь к шаблонам контроллера
|
||||
|
||||
/** Соединение с базой данных */
|
||||
public Database $db;
|
||||
|
||||
// Фильтры
|
||||
/** @var ?\ctiso\Filter\ActionAccess Обьект хранит параметры доступа */
|
||||
public $access = null;
|
||||
/** @var ?\ctiso\Filter\ActionLogger Обьект для ведения лога */
|
||||
public $logger = null;
|
||||
/** @var Factory Обьект для создания моделей */
|
||||
private $factory = null;
|
||||
|
||||
/** @var ?Url Параметры для ссылки */
|
||||
public $part = null;
|
||||
|
||||
/** @var \ctiso\Registry Ссылка на настройки */
|
||||
public $config;
|
||||
/** @var \ctiso\Role\User Обьект пользователя */
|
||||
public $user;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->part = new Url();
|
||||
}
|
||||
|
||||
public function setUp(): void {
|
||||
}
|
||||
public $moduleTitle = "";
|
||||
|
||||
/**
|
||||
* Загрузка файла настроек
|
||||
* @param string $name
|
||||
* @return array
|
||||
* Соединение с базой данных
|
||||
*/
|
||||
public function loadConfig($name) {
|
||||
$basePath = $this->config->get('site', 'path');
|
||||
public $db;
|
||||
|
||||
$filename = Path::join($basePath, 'modules', $name);
|
||||
// Фильтры
|
||||
public $access = null; // Обьект хранит параметры доступа
|
||||
public $logger = null; // Обьект для ведения лога
|
||||
|
||||
private $factory = null; // Ссылка на обьект создания модели
|
||||
private $helpers = array(); // Помошники для действий
|
||||
public $param = array(); // Параметры для ссылки
|
||||
|
||||
public $_registry/*: Registry*/; // Ссылка на реестр
|
||||
public $_shortcut;
|
||||
public $modulePrefix = '';
|
||||
public $iconPath = '';
|
||||
|
||||
// Для Widgets
|
||||
public $view = null;
|
||||
public $childNodes = array();
|
||||
public $ctrlValues = array();
|
||||
public $childViews = array();
|
||||
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
}
|
||||
|
||||
public function loadConfig($name) {
|
||||
$filename = Shortcut::getUrl('config', $name);
|
||||
$settings = [];
|
||||
if (file_exists($filename)) {
|
||||
$settings = include($filename);
|
||||
include($filename);
|
||||
} else {
|
||||
throw new Exception('Невозможно загрузить файл настроек ' . $name);
|
||||
}
|
||||
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);
|
||||
return Path::join(CMS_PATH, "modules", $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет подсказки
|
||||
* @param View $view
|
||||
* @param string $name
|
||||
*/
|
||||
public function addSuggest(View $view, $name): void {
|
||||
$file = Path::join($this->modulePath, 'help', $name . '.suggest');
|
||||
public function addSuggest(View_View $view, $name)
|
||||
{
|
||||
$suggest = array();
|
||||
$file = Path::join($this->viewPath, 'help', $name . '.suggest');
|
||||
if (file_exists($file)) {
|
||||
$view->suggestions = include($file);
|
||||
include($file);
|
||||
$view->suggestions = $suggest;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск иконки
|
||||
* @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');
|
||||
function findIcon($icon, $size)
|
||||
{
|
||||
return Path::join($this->iconPath, $size . 'x' . $size, $icon . '.png');
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает представление
|
||||
* @param string $name
|
||||
* @param class-string $viewClass
|
||||
* @return Composite
|
||||
* @param $name String
|
||||
* @param $viewClass String
|
||||
* @return View_Composite
|
||||
*/
|
||||
public function getView($name, $viewClass = Composite::class)
|
||||
public function getView($name, $viewClass = 'View_Composite')
|
||||
{
|
||||
$file = $name . self::TEMPLATE_EXTENSION;
|
||||
|
||||
$basePath = $this->config->get('system', 'path');
|
||||
$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($basePath, "templates") => Path::join($webPath, "templates")
|
||||
];
|
||||
$list = array(
|
||||
Path::join($this->viewPath, TEMPLATES, $this->viewPathPrefix) => Path::join(WWW_PATH, "modules", $this->name, TEMPLATES, $this->viewPathPrefix),
|
||||
Path::join(CMS_PATH, "templates") => Path::join(WWW_PATH, "templates")
|
||||
);
|
||||
|
||||
// Поиск файла для шаблона
|
||||
foreach($list as $ospath => $path) {
|
||||
|
|
@ -141,41 +114,34 @@ class Action implements ActionInterface
|
|||
if(file_exists($template)) { break; }
|
||||
}
|
||||
|
||||
/** @var \ctiso\View\Composite */
|
||||
$tpl = new $viewClass($template);
|
||||
$tpl->config = $this->config;
|
||||
$tpl/*: View_Composite*/ = new $viewClass($template);
|
||||
|
||||
$stylePath = Path::join($webPath, "assets", "css");
|
||||
$iconsPath = Path::join($webPath, 'icons');
|
||||
$scriptPath = Path::join($webPath, 'assets');
|
||||
|
||||
$tpl->set('icons', $iconsPath); // Путь к файлам текущей темы
|
||||
$tpl->set('assets', $stylePath);
|
||||
$tpl->set('script', $scriptPath); // Путь к файлам скриптов
|
||||
$assets = Path::join(enableHttps(WWW_PATH), "assets", "css");
|
||||
$tpl->set('icons', $this->iconPath); // Путь к файлам текущей темы
|
||||
$tpl->set('media', $this->themePath); // Путь к файлам текущей темы
|
||||
$tpl->set('assets', $assets);
|
||||
$tpl->set('script', $this->jsPath); // Путь к файлам скриптов
|
||||
$tpl->set('template', $path); // Путь к файлам текущего шаблона
|
||||
|
||||
$tpl->setAlias([
|
||||
'assets' => $stylePath,
|
||||
'icons' => $iconsPath,
|
||||
'script' => $scriptPath,
|
||||
$tpl->setAlias(array(
|
||||
'assets' => $assets,
|
||||
'icons' => $this->iconPath,
|
||||
'script' => $this->jsPath,
|
||||
// Для media и template поиск происходит как для файлов шаблонов
|
||||
'media' => $list,
|
||||
'template' => $list
|
||||
]);
|
||||
));
|
||||
|
||||
$tpl->loadImports(Path::skipExtension($template) . ".import");
|
||||
|
||||
$this->addSuggest($tpl, $name);
|
||||
return $tpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param class-string<T> $name
|
||||
* @return T
|
||||
*/
|
||||
public function getModel($name)
|
||||
{
|
||||
if (!$this->factory) {
|
||||
$this->factory = new Factory($this->db, $this->config, $this->user);
|
||||
$this->factory = new Model_Factory($this->db, $this->_registry);
|
||||
}
|
||||
return $this->factory->getModel($name);
|
||||
}
|
||||
|
|
@ -185,118 +151,125 @@ class Action implements ActionInterface
|
|||
* Т.к действия являются методами класса то
|
||||
* 1. Можно переопределить действия
|
||||
* 2. Использовать наследование чтобы добавить к старому обработчику новое поведение
|
||||
* @param HttpRequest $request запроса
|
||||
* @return View|string
|
||||
* @param $request Обьект запроса
|
||||
*/
|
||||
public function preProcess(HttpRequest $request)
|
||||
public function preprocess(HttpRequest $request)
|
||||
{
|
||||
$action = self::ACTION_PREFIX . ucfirst($request->getAction());
|
||||
if (!method_exists($this, $action)) {
|
||||
$action = "actionIndex";
|
||||
}
|
||||
$view = $this->forward($action, $request);
|
||||
if ($view instanceof View) {
|
||||
$view->active_module = get_class($this);
|
||||
$view->module_action = $action;
|
||||
}
|
||||
return $view;
|
||||
if ($view instanceof View_View) {
|
||||
$view->active_module = get_class($this);
|
||||
$view->module_action = $action;
|
||||
}
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполнение действия
|
||||
* @param HttpRequest $request
|
||||
* @return View|string|false
|
||||
*/
|
||||
|
||||
public function execute(HttpRequest $request)
|
||||
{
|
||||
$result = $this->preProcess($request);
|
||||
return $result;
|
||||
$result = $this->preprocess($request);
|
||||
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(array($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 {
|
||||
$this->part->addQueryParam($key, $value);
|
||||
public function postUrl($name, $param)
|
||||
{
|
||||
$uri = array_merge(array('module' =>
|
||||
strtr($this->modulePrefix . strtolower(get_class($this)), array('module_' => '')), "action" => $name),
|
||||
$this->param, $param);
|
||||
|
||||
return "?" . http_build_query($uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация ссылки c учетом прав пользователя на ссылки
|
||||
* @param string $actionName Действие
|
||||
* @param string $name Действие
|
||||
* @param array $param Дополнительные параметры
|
||||
* 'mode' означает что элемент до отправки обрабатывается javascript
|
||||
* @return Url|null
|
||||
* @return array|null
|
||||
*/
|
||||
public function nUrl($actionName, array $param = [])
|
||||
public function nUrl($name, array $param = array())
|
||||
{
|
||||
$access = $this->access;
|
||||
$url = new Url();
|
||||
$access/*: Filter_ActionAccess*/ = $this->access;
|
||||
|
||||
if ($access == null || $access->checkAction($actionName)) {
|
||||
$moduleName = explode("\\", strtolower(get_class($this)));
|
||||
if (count($moduleName) > 2) {
|
||||
array_shift($moduleName);
|
||||
if ($moduleName[0] == $moduleName[1]) {
|
||||
array_shift($moduleName);
|
||||
}
|
||||
}
|
||||
$param = array_merge(['module' => implode("\\", $moduleName), "action" => $actionName], $param);
|
||||
|
||||
$url->setParent($this->part);
|
||||
$url->setQuery($param);
|
||||
if ($access == null || $access->checkAction($name)) {
|
||||
return Functions::lcurry(array($this, 'postUrl'), $name, $param);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return $url;
|
||||
public function fUrl($name, array $param = array())
|
||||
{
|
||||
return forceUrl($this->nUrl($name, $param));
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет параметр для всех ссылок создаваемых функцией nUrl, aUrl
|
||||
*/
|
||||
public function addParameter($name, $value)
|
||||
{
|
||||
if ($value) {
|
||||
$this->param [$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация ссылки на действие контроллера
|
||||
* Ajax определяется автоматически mode = ajax используется для смены layout
|
||||
* @param string $name
|
||||
* @param $name
|
||||
* @param array $param
|
||||
* @return Url|null
|
||||
* @return array|null
|
||||
*
|
||||
* @example ?action=$name&mode=ajax
|
||||
* {$param[i].key = $param[i].value}
|
||||
*/
|
||||
public function aUrl($name, array $param = [])
|
||||
public function aUrl($name, array $param = array())
|
||||
{
|
||||
return $this->nUrl($name, array_merge(['mode' => 'ajax'], $param));
|
||||
return $this->nUrl($name, array_merge(array('mode' => 'ajax'), $param)); // FIXME
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление помошника контроллера
|
||||
*/
|
||||
public function addHelper($class)
|
||||
{
|
||||
$this->helpers [] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вызов помошников контроллера
|
||||
*/
|
||||
public function callHelpers(HttpRequest $request)
|
||||
{
|
||||
$action = self::ACTION_PREFIX . $request->getAction();
|
||||
foreach ($this->helpers as $helper) {
|
||||
if (method_exists($helper, $action)) {
|
||||
return call_user_func(array($helper, $action), $request, $this);
|
||||
} else {
|
||||
return $helper->actionIndex($request, $this); // Вместо return response ???
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Загрузка файла класса
|
||||
* @deprecated Веместо его нужно использовать автозагрузку
|
||||
* @param string $path
|
||||
* @param mixed $setup
|
||||
* @param string $prefix
|
||||
* @return mixed
|
||||
*/
|
||||
public function loadClass($path, $setup = null, $prefix = '')
|
||||
{
|
||||
|
|
@ -308,11 +281,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,22 +288,70 @@ 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 к отображению
|
||||
*/
|
||||
function getPageId(HttpRequest $request) {
|
||||
$pageId = time();
|
||||
public function addChild(/*Widget*/ $section, $node)
|
||||
{
|
||||
$this->childNodes[$section] = $node;
|
||||
}
|
||||
|
||||
public function setValue(/*Widget*/ $name, $value)
|
||||
{
|
||||
$this->ctrlValues[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление дочернего отображения к текущему отображению
|
||||
*/
|
||||
public function addView(/*CompositeView*/ $section, $node)
|
||||
{
|
||||
$this->childViews[$section] = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация содержания
|
||||
* Путаница c execute и render
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
if ($this->view instanceof View_View) {
|
||||
$this->view->assignValues($this->ctrlValues);
|
||||
|
||||
$node/*: Widgets_Widget*/ = 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)
|
||||
function checkPageId(HttpRequest $request, $page)
|
||||
{
|
||||
if ($request->get('__forced__')) {
|
||||
return true;
|
||||
|
|
@ -346,15 +362,19 @@ class Action implements ActionInterface
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return State
|
||||
*/
|
||||
function _getActionPath() {
|
||||
return new State('index');
|
||||
function _getActionPath()
|
||||
{
|
||||
return new Controller_State('index');
|
||||
}
|
||||
|
||||
function redirect(string $action): void {
|
||||
header('location: ' . $action);
|
||||
// Тоже убрать в метод Controller_Model
|
||||
function getActionPath(HttpRequest $request, $action = null)
|
||||
{
|
||||
$this->_getActionPath()->getPath($this, ($action) ? $action : $request->getAction());
|
||||
}
|
||||
|
||||
function redirect($action) {
|
||||
header('location: ' . $this->fUrl($action));
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1,90 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Controller;
|
||||
function replaceContent($match) {
|
||||
$result = phptal_component(htmlspecialchars_decode($match[3]));
|
||||
return $result;
|
||||
}
|
||||
|
||||
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;
|
||||
function applyComponents($text) {
|
||||
return preg_replace_callback('/<(\w+)(\s+[a-zA-Z\-]+=\"[^\"]*\")*\s+tal:replace="structure\s+component:([^\"]*)"[^>]*>/u', 'replaceContent', $text);
|
||||
}
|
||||
|
||||
class ComponentRequest {
|
||||
public $component_id;
|
||||
public $component_title;
|
||||
public $r;
|
||||
|
||||
function __construct($c, HttpRequest $r) {
|
||||
$this->component_id = $c;
|
||||
$this->r = $r;
|
||||
}
|
||||
|
||||
function get($key, $default = null) {
|
||||
if ($key == 'active_page') {
|
||||
return $this->r->get($key);
|
||||
}
|
||||
$arr = $this->r->get($key);
|
||||
if ($arr !== NULL) {
|
||||
if (is_array($arr)) {
|
||||
return Arr::get($arr, $this->component_id, $default);
|
||||
} else {
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
function getAction() {
|
||||
return $this->r->getAction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс компонента
|
||||
*/
|
||||
class Component
|
||||
class Controller_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;
|
||||
public $registry/*: Settings*/;
|
||||
public $db/*: Database*/;
|
||||
public $parameter/*: Collection*/;
|
||||
|
||||
/** @var string */
|
||||
public $output = 'html';
|
||||
|
||||
/** @var string */
|
||||
public $module;
|
||||
/** @var string */
|
||||
public $item_module;
|
||||
|
||||
/**
|
||||
* @var SiteInterface $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]));
|
||||
function get($request, $key, $default) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет запрос компонента и возвращает результат
|
||||
* Результат может быть строкой или View для обычных компонентов, или массивом для использования в сервисах
|
||||
*
|
||||
* @param HttpRequest $request
|
||||
* @param bool $has_id
|
||||
* @return mixed
|
||||
*/
|
||||
function execute(HttpRequest $request, $has_id = true) {
|
||||
$crequest = new ComponentRequest($this->component_id, $request);
|
||||
|
||||
|
|
@ -94,74 +76,53 @@ class Component
|
|||
} else {
|
||||
$action = 'action' . ucfirst($_action);
|
||||
}
|
||||
|
||||
|
||||
$this->before();
|
||||
$actionMethod = [$this, $action];
|
||||
if (is_callable($actionMethod)) {
|
||||
return call_user_func($actionMethod, $crequest);
|
||||
if (method_exists($this, $action)) {
|
||||
return call_user_func(array($this, $action), $crequest);
|
||||
} else {
|
||||
return $this->actionIndex($crequest);
|
||||
}
|
||||
|
||||
return $this->actionIndex($crequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить имя шаблона
|
||||
* @param Registry $_registry
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplateName($_registry) {
|
||||
return (isset($_COOKIE['with_template']) && preg_match('/^[\w\d-]{3,20}$/', $_COOKIE['with_template']))
|
||||
? $_COOKIE['with_template'] : ($_registry ? $_registry->get('site', 'template') : 'modern');
|
||||
}
|
||||
public function getTemplateName($_registry/*: Settings*/) {
|
||||
return (isset($_COOKIE['with_template']) && preg_match('/^[\w\d-]{3,20}$/', $_COOKIE['with_template'])) ? $_COOKIE['with_template'] : $_registry->readKey(array('system', 'template'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить шаблон
|
||||
* @param string $name
|
||||
* @return PHPTAL|JsonView
|
||||
*/
|
||||
public function getView($name)
|
||||
{
|
||||
if ($this->output === 'json') {
|
||||
return new JsonView($name);
|
||||
}
|
||||
|
||||
/** @var Registry $config */
|
||||
$config = $this->config;
|
||||
$default = $config->get('site', 'template');
|
||||
$template = ($this->template) ? $this->template : $this->getTemplateName($config);
|
||||
//
|
||||
$registry/*: Settings*/ = $this->registry;
|
||||
$template = ($this->template) ? $this->template : $this->getTemplateName($registry);
|
||||
|
||||
$selected = null;
|
||||
$tpl = null;
|
||||
foreach ($this->viewPath as $index => $viewPath) {
|
||||
// Загружать шаблон по умолчанию если не найден текущий
|
||||
$dir = Path::join($this->viewPath[$index], 'templates', $template);
|
||||
if (is_dir($dir)) {
|
||||
if(is_dir(Path::join($this->viewPath[$index], 'templates', $template))) {
|
||||
$tpl = new PHPTAL(Path::join($this->viewPath[$index], 'templates', $template, $name));
|
||||
$tpl->setPhpCodeDestination(PHPTAL_PHP_CODE_DESTINATION);
|
||||
$selected = $index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($selected === null) {
|
||||
// Последний вариант viewPath, путь к папке компонента
|
||||
$selected = count($this->viewPath) - 1;
|
||||
$tpl = new PHPTAL(Path::join($this->viewPath[$selected], 'templates', 'modern', $name));
|
||||
$tpl = new PHPTAL(Path::join($this->viewPath[0], 'templates', 'modern', $name));
|
||||
$tpl->setPhpCodeDestination(PHPTAL_PHP_CODE_DESTINATION);
|
||||
$template = 'modern';
|
||||
$selected = 0;
|
||||
}
|
||||
|
||||
$tpl->stripComments(true);
|
||||
$tpl->addPreFilter(new \PHPTAL_PreFilter_Normalize());
|
||||
$tpl->addPreFilter(new PHPTAL_PreFilter_Normalize());
|
||||
|
||||
$tpl->set('common', Path::join($this->config->get('system', 'web'), '../', 'common'));
|
||||
$tpl->set('script', Path::join($this->config->get('system', 'web'), 'js'));
|
||||
$tpl->set('media', Path::join($this->config->get('system', 'templates.web'), $template));
|
||||
|
||||
if ($default) {
|
||||
$tpl->set('site_template', Path::join($this->config->get('site', 'templates.web'), $default));
|
||||
$tpl->set('common', Path::join(WWW_PATH, '../', 'common'));
|
||||
$tpl->set('script', Path::join(WWW_PATH, 'js'));
|
||||
$tpl->set('media', Path::join(TEMPLATE_WEB, $template));
|
||||
if ($registry) {
|
||||
$tpl->set('site_template', SITE_WWW_PATH . '/templates' . $registry->readKey(array('system', 'template')));
|
||||
}
|
||||
$tpl->set('base', $this->config->get('site', 'web'));
|
||||
$tpl->set('base', SITE_WWW_PATH);
|
||||
|
||||
$tpl->set('component_base', $this->webPath[$selected]);
|
||||
$tpl->set('component', Path::join($this->webPath[$selected], 'templates', $template));
|
||||
|
|
@ -170,122 +131,69 @@ 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;
|
||||
$registry/*: Settings*/ = $this->registry;
|
||||
// Брать настройки из куков если есть
|
||||
$template = ($this->template) ? $this->template : $this->getTemplateName($registry);
|
||||
foreach ($this->viewPath as $index => $_) {
|
||||
foreach ($this->viewPath as $index => $viewPath) {
|
||||
if(is_dir(Path::join($this->viewPath[$index], 'templates', $template))) {
|
||||
return Path::join($this->viewPath[$index], 'templates', $template, $name);
|
||||
}
|
||||
}
|
||||
|
||||
return Path::join($this->viewPath[count($this->viewPath) - 1], 'templates', 'modern', $name);
|
||||
|
||||
return Path::join($this->viewPath[0], 'templates', 'modern', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает путь к шаблонам
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplateWebPath()
|
||||
{
|
||||
return Path::join($this->webPath[count($this->webPath) - 1], 'templates', 'modern');
|
||||
return Path::join($this->webPath[0], 'templates', 'modern');
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает модель
|
||||
*
|
||||
* @template T
|
||||
* @param class-string<T> $modelName
|
||||
* @return T
|
||||
* @param string $name
|
||||
* @return model
|
||||
*/
|
||||
public function getModel($modelName)
|
||||
public function getModel($name)
|
||||
{
|
||||
$modelName = "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) {
|
||||
$result = [];
|
||||
public function options($key, $val, $res/*: Database_PDOStatement*/) {
|
||||
$result = array();
|
||||
while($res->next()) {
|
||||
$result[] = ['value' => $res->getString($key), 'name' => $res->getString($val)];
|
||||
$result[] = array('value' => $res->getString($key), 'name' => $res->getString($val));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $list
|
||||
* @param bool $selected
|
||||
* @return array{value: string, name: string, selected: bool}[]
|
||||
*/
|
||||
public function optionsPair(array $list, $selected = false) {
|
||||
$result = [];
|
||||
public function optionsPair($list, $selected = false) {
|
||||
$result = array();
|
||||
foreach ($list as $key => $value) {
|
||||
$result [] = ['value' => $key, 'name' => $value, 'selected' => $key == $selected];
|
||||
$result [] = array('value' => $key, 'name' => $value, 'selected' => $key == $selected);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Найти файл по пути
|
||||
* @param string[] $pathList
|
||||
* @param string $name
|
||||
* @return string|null
|
||||
*/
|
||||
function findFile(array $pathList, string $name) {
|
||||
foreach($pathList as $item) {
|
||||
$filename = Path::join($item, $name);
|
||||
if (file_exists($filename)) {
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить информацию о параметрах
|
||||
* @return array<mixed>
|
||||
*/
|
||||
function getInfo() {
|
||||
$filename = Path::join($this->viewPath[count($this->viewPath) - 1], 'install.json');
|
||||
$filename = Path::join($this->viewPath[0], 'install.json');
|
||||
if (file_exists($filename)) {
|
||||
$settings = json_decode(File::getContents($filename), true);
|
||||
if ($settings) {
|
||||
return $settings;
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
return ['parameter' => []];
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация интерфейса для выбора галлереи фотографии
|
||||
* @param Composite $view
|
||||
* @param ?\ctiso\Form\OptionsFactory $options
|
||||
*/
|
||||
public function setParameters(Composite $view, $options = null): void
|
||||
public function setParameters($view/*: View_Composite*/)
|
||||
{
|
||||
$form = new Form();
|
||||
$form = new Form_Form();
|
||||
$options = new Form_OptionFactory($this->db, $this->registry);
|
||||
|
||||
$settings = $this->getInfo();
|
||||
$form->addFieldList($settings['parameter'], $options);
|
||||
|
|
@ -295,94 +203,56 @@ 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);
|
||||
|
||||
return $form->field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обьеденить с ComponentFactory
|
||||
* @param string $expression
|
||||
* @param SiteInterface $site
|
||||
* @return Component
|
||||
*/
|
||||
static function loadComponent(string $expression, $site)
|
||||
static function loadComponent($expression, Database $db, $registry/*: Registry*/)
|
||||
{
|
||||
|
||||
$expression = htmlspecialchars_decode($expression);
|
||||
$offset = strpos($expression, '?');
|
||||
$url = parse_url($expression);
|
||||
|
||||
$arguments = [];
|
||||
$path = $expression;
|
||||
if (is_int($offset)) {
|
||||
|
||||
$arguments = array();
|
||||
if ($offset === false) {
|
||||
$path = $expression;
|
||||
} else if (is_int($offset)) {
|
||||
$path = substr($expression, 0, $offset);
|
||||
$query = substr($expression, $offset + 1);
|
||||
parse_str($query, $arguments);
|
||||
}
|
||||
$name = $path;
|
||||
$config = $site->getConfig();
|
||||
|
||||
// FIXME: Если имя для компонента не задано то возвращаем пустой компонент
|
||||
// Нужно дополнительно проверить и файл или в autoloader просто не найдет файл копонента
|
||||
if (!$name) {
|
||||
return new Component();
|
||||
}
|
||||
$path = Path::join (BASE_PATH, 'components', $name, $name . '.php');
|
||||
$className = 'Component_' . $name;
|
||||
|
||||
$filename = ucfirst($name);
|
||||
$path = Path::join ($config->get('site', 'components'), $name, $filename . '.php');
|
||||
$className = implode("\\", ['Components', ucfirst($name), $filename]);
|
||||
|
||||
$component = null;
|
||||
$component/*: Controller_Component*/ = null;
|
||||
|
||||
if (file_exists($path)) {
|
||||
/** @var Component $component */
|
||||
$component = new $className();
|
||||
require_once ($path);
|
||||
|
||||
$component->viewPath = [$config->get('site', 'components') . '/' . $name . '/'];
|
||||
$component->webPath = [$config->get('site', 'components.web') . '/' . $name];
|
||||
$component->COMPONENTS_WEB = $config->get('site', 'web') . '/components/';
|
||||
$component = new $className();
|
||||
$component->db = $db;
|
||||
$component->registry = $registry;
|
||||
|
||||
$component->viewPath = array(BASE_PATH . '/components/' . $name . '/');
|
||||
$component->webPath = array(SITE_WWW_PATH . '/components/' . $name);
|
||||
|
||||
$component->COMPONENTS_WEB = SITE_WWW_PATH . '/components/';
|
||||
|
||||
} else {
|
||||
/** @var Component $component */
|
||||
$path = Path::join (COMPONENTS, $name, $name . '.php');
|
||||
require_once ($path);
|
||||
$component = new $className();
|
||||
$template = $component->getTemplateName($site->getConfig());
|
||||
$component->db = $db;
|
||||
$component->registry = $registry;
|
||||
|
||||
$component->viewPath = [
|
||||
// Сначало ищем локально
|
||||
$config->get('site', 'templates') . '/'. $template . '/_components/' . $name . '/',
|
||||
$config->get('site', 'components') . '/' . $name . '/',
|
||||
// Потом в общем хранилище
|
||||
$config->get('system', 'templates'). '/' . $template . '/_components/' . $name . '/',
|
||||
$config->get('system', 'components') . '/' . $name . '/',
|
||||
];
|
||||
$component->viewPath = array(COMPONENTS . '/' . $name . '/', BASE_PATH . '/components/' . $name . '/');
|
||||
if (defined('COMPONENTS_WEB')) {
|
||||
$component->webPath = [
|
||||
// Сначало локально
|
||||
$config->get('site', 'templates.web') . '/' . $template . '/_components/' . $name,
|
||||
$config->get('site', 'components.web') . '/' . $name,
|
||||
// Потом в общем хранилище
|
||||
$config->get('system', 'templates.web') . '/' . $template . '/_components/' . $name,
|
||||
$config->get('system', 'components.web') . '/' . $name,
|
||||
];
|
||||
$component->COMPONENTS_WEB = $config->get('system', 'components.web');
|
||||
$component->webPath = array(COMPONENTS_WEB . '/' . $name, SITE_WWW_PATH . '/components/' . $name);
|
||||
$component->COMPONENTS_WEB = COMPONENTS_WEB;
|
||||
} else {
|
||||
$component->webPath = ['', $config->get('site', 'components.web') . '/' . $name, '', ''];
|
||||
$component->webPath = array('', SITE_WWW_PATH . '/components/' . $name, '');
|
||||
}
|
||||
}
|
||||
|
||||
// Вынести в отдельную функцию
|
||||
$db = $site->getDatabase();
|
||||
|
||||
$component->db = $db;
|
||||
$component->config = $site->getConfig();
|
||||
$component->site = $site;
|
||||
|
||||
}
|
||||
|
||||
$stmt = $db->prepareStatement("SELECT * FROM component WHERE code = ?");
|
||||
$stmt->setString(1, $expression);
|
||||
$cid = $stmt->executeQuery();
|
||||
|
|
@ -390,7 +260,6 @@ class Component
|
|||
$component->component_id = $cid->getInt('id_component');
|
||||
} else {
|
||||
$last = $db->getIdGenerator();
|
||||
$result = null;
|
||||
if ($last->isBeforeInsert()) {
|
||||
$result = $last->getId('component_id_component_seq');
|
||||
|
||||
|
|
@ -410,40 +279,38 @@ class Component
|
|||
|
||||
$params = new Collection();
|
||||
$params->import(array_merge($_GET, $arguments));
|
||||
|
||||
$component->parameter = $params;
|
||||
$component->template = $params->get('template', false);
|
||||
|
||||
$editor = $component->getEditUrl();
|
||||
if ($editor) {
|
||||
$site->addComponentConfig($editor);
|
||||
if(class_exists("Controller_Site")){ //Если мы в CMS2
|
||||
$instance = Controller_Site::getInstance();
|
||||
$instance->componentsConfig[] = $editor;
|
||||
} else {
|
||||
global $componentsConfig;
|
||||
$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;
|
||||
$param = array();
|
||||
$parameter/*: Collection*/ = $this->parameter;
|
||||
foreach($parameter->export() as $key => $value) {
|
||||
$param[$key] = $value;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$data = array();
|
||||
foreach($arr as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$data[$key] = Arr::get($value, $this->component_id);
|
||||
|
|
@ -456,12 +323,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');
|
||||
|
||||
|
|
@ -474,27 +336,14 @@ class Component
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
* @param array $shim
|
||||
*/
|
||||
function addRequireJsPath($name, $path, $shim = null): void {
|
||||
$this->site->addRequireJsPath($name, $path, $shim);
|
||||
* @deprecated В CMS2 перенесено в контроллер сайта!
|
||||
*/
|
||||
|
||||
function addRequireJsPath($name, $path, $shim = null) {
|
||||
Controller_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*/) {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,119 +4,107 @@
|
|||
* Первичный контроллер контроллер страниц
|
||||
* @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;
|
||||
|
||||
class Front extends Action
|
||||
class Controller_Front extends Controller_Action
|
||||
{
|
||||
|
||||
/**
|
||||
* Параметр по которому выбирается модуль
|
||||
* @var string
|
||||
*/
|
||||
protected $_param;
|
||||
/**
|
||||
* Значение параметра по умолчанию
|
||||
* @var string
|
||||
*/
|
||||
protected $default;
|
||||
/** @var Shortcut */
|
||||
protected $shortcut; // Ярлык к модулю
|
||||
protected $_param; // Параметр по которому выбирается модуль
|
||||
protected $default; // Значение параметра по умолчанию
|
||||
|
||||
/** @var array<string, Action> */
|
||||
protected $modules = [];
|
||||
protected $modules = array();
|
||||
|
||||
/**
|
||||
* @param string $default
|
||||
* @param Settings $_registry
|
||||
* @param Shortcut $_shortcut
|
||||
*/
|
||||
public function __construct(Database $db, Registry $config, User $user, $default) {
|
||||
public function __construct(Settings $_registry, $_shortcut) // $db, $installer, $shortcut
|
||||
{
|
||||
parent::__construct();
|
||||
$this->config = $config;
|
||||
$this->db = $db;
|
||||
$this->user = $user;
|
||||
$this->default = $default;
|
||||
$registry = $_registry;
|
||||
$this->_registry = $_registry;
|
||||
$this->_shortcut = $_shortcut; // $cc->newShortcut();
|
||||
|
||||
$dsn = $registry->readKey(array('system', 'dsn'));
|
||||
$this->db = Database::getConnection($dsn); // $cc->newConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет загружен ли модуль
|
||||
* @param string $name Имя модуля
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoaded($name): bool
|
||||
public function isLoaded($name)
|
||||
{
|
||||
return isset($this->modules[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает экземпляр модуля и выполняет действия для него
|
||||
* @param string $name Имя модуля
|
||||
* @param HttpRequest $request
|
||||
* @return string
|
||||
* @param string $name Имя модуля
|
||||
* @param request $request Имя модуля
|
||||
* @return string
|
||||
*/
|
||||
public function loadModule($name, HttpRequest $request)
|
||||
public function loadModule($name, Collection $request, $controller = false)
|
||||
{
|
||||
if ($this->isLoaded($name)) {
|
||||
$module = $this->modules[$name];
|
||||
return $module->access->execute($request);
|
||||
}
|
||||
|
||||
$parts = explode('\\', $name);
|
||||
$suffix = ($controller) ? $controller : $name;
|
||||
$moduleFile = Shortcut::getUrl($this->shortcut, $name, $suffix); // ModuleLoader (2)
|
||||
|
||||
$config = $this->config;
|
||||
$module = $this->loadClass($moduleFile, null, 'Module_');
|
||||
if ($module) {
|
||||
// Инициализация модуля
|
||||
$module->front = $this;
|
||||
$module->viewPath = Shortcut::getUrl('modulepath', $name);
|
||||
$module->name = $name;
|
||||
|
||||
$moulesPath = Path::join($config->get('system', 'path'), 'modules');
|
||||
$logPath = Path::join($config->get('site', 'path'), $config->get('system', 'access.log'));
|
||||
$module->param = $this->param;
|
||||
//
|
||||
$module->_registry = $this->_registry;
|
||||
$module->_shortcut = $this->_shortcut;
|
||||
|
||||
$first = $parts[0];
|
||||
$second = (count($parts) >= 2) ? $parts[1] : $parts[0];
|
||||
$module->iconPath = $this->iconPath; // -> Registry
|
||||
$module->themePath = $this->themePath; // -> Registry
|
||||
$module->jsPath = $this->jsPath; // -> Registry
|
||||
$module->db = $this->db;
|
||||
// Не для всех приложений нужно вести лог действий
|
||||
// Ведение лога
|
||||
$logger = $this->loadClass(__DIR__ . '/../Filter/ActionLogger.php', $module, 'Filter_');
|
||||
$logger->before = $this->loadSettings(Shortcut::getUrl('logger', $name));
|
||||
// Управление доступом
|
||||
$module->access = $this->loadClass(__DIR__ . '/../Filter/ActionAccess.php', $logger, 'Filter_');
|
||||
$module->access->name = $suffix;
|
||||
$module->access->access = $this->loadSettings(Shortcut::getUrl('access', $name));
|
||||
|
||||
$ucname = ucfirst($first);
|
||||
$ucpart = ucfirst($second);
|
||||
|
||||
$moduleClass = "Modules\\$ucname\\$ucpart";
|
||||
/** @var Action $module */
|
||||
$module = new $moduleClass();
|
||||
|
||||
// Инициализация модуля
|
||||
$modPath = Path::join($moulesPath, $first);
|
||||
$module->modulePath = $modPath;
|
||||
$module->name = $name;
|
||||
//
|
||||
$module->config = $this->config;
|
||||
$module->db = $this->db;
|
||||
$module->user = $this->user;
|
||||
$module->front = $this;
|
||||
// Ведение лога
|
||||
$logger = new ActionLogger($module, $logPath, $this->user);
|
||||
$filename = Path::join($modPath, 'filters', 'logger.json');
|
||||
$logger->before = $this->loadSettings($filename);
|
||||
// Управление доступом
|
||||
$module->access = new ActionAccess($logger, $this->user);
|
||||
$module->access->access = $this->loadSettings(Path::join($modPath, 'filters', 'access.json'));
|
||||
|
||||
$module->setUp();
|
||||
|
||||
$this->modules[$name] = $module;
|
||||
$result = $module->access->execute($request);
|
||||
return $result;
|
||||
$module->setUp();
|
||||
|
||||
$this->modules[$name] = $module;
|
||||
$result = $module->access->execute($request);
|
||||
return $result;
|
||||
}
|
||||
return null; // throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
public function setParameter($shortcut, $param, $name)
|
||||
{
|
||||
$this->shortcut = $shortcut;
|
||||
// Параметр
|
||||
$this->_param = $param;
|
||||
$this->default = $name;
|
||||
}
|
||||
|
||||
public function execute(HttpRequest $request)
|
||||
{
|
||||
$name = $request->getString('module', $this->default);
|
||||
try {
|
||||
return $this->loadModule($name, $request);
|
||||
$name = explode("_", $request->get($this->_param, $this->default));
|
||||
if (count($name) >= 2) {
|
||||
$controller = $name[1];
|
||||
} else {
|
||||
$controller = false;
|
||||
}
|
||||
try{
|
||||
return $this->loadModule($name[0], $request, $controller);
|
||||
} catch (UserMessageException $ex) { //Исключение с понятным пользователю сообщением
|
||||
$mode = $request->get('mode');
|
||||
if ($mode == 'ajax' || $mode == 'json') {
|
||||
if($mode == 'ajax' || $mode == 'json'){
|
||||
return json_encode(['result'=>'fail', 'message'=> $ex->userMessage]);
|
||||
} else {
|
||||
return $ex->userMessage;
|
||||
|
|
|
|||
|
|
@ -1,67 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Controller;
|
||||
|
||||
use ctiso\Settings;
|
||||
use ctiso\Path;
|
||||
use ctiso\Database\JsonInstall;
|
||||
use ctiso\Database\Manager;
|
||||
|
||||
class Installer
|
||||
class Controller_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);
|
||||
$item = $this->_registry->readKey(array($name));
|
||||
if ($item) {
|
||||
$setup = $this->getSetupFile($name);
|
||||
if (file_exists($setup) && (filemtime($setup) > $item['time'])) {
|
||||
|
|
@ -72,18 +41,10 @@ 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 = [];
|
||||
$json_installer = new JsonInstall($this->db_manager);
|
||||
$json_installer = new Database_JsonInstall($this->db_manager);
|
||||
foreach ($sql as $version => $install) {
|
||||
if (version_compare($version, $version_new, "<=") && version_compare($version, $version_old, ">")) {
|
||||
$file = Path::join(call_user_func($this->installPath, $name), "sql", $install);
|
||||
|
|
@ -94,78 +55,60 @@ 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 = new Database_JsonInstall($this->db_manager);
|
||||
$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 = [];
|
||||
$result = array();
|
||||
$setup = $this->getSetupFile($name);
|
||||
|
||||
if (file_exists($setup) && ($this->isChanged($name) || $force)) {
|
||||
$registry = $this->_registry;
|
||||
|
||||
$settings = new Settings($setup);
|
||||
$settings->read();
|
||||
|
||||
$item = $registry->get($name);
|
||||
$item = $registry->readKey(array($name));
|
||||
|
||||
$version_new = $settings->get('version');
|
||||
if ($item) {
|
||||
$version_old = $item['version'];
|
||||
$version_old = $item['version'];
|
||||
} else {
|
||||
$version_old = "0.0";
|
||||
$registry->writeKey([$name], []);
|
||||
$registry->writeKey(array($name), array());
|
||||
}
|
||||
if (version_compare($version_old, $settings->get('version'), "!=")) {
|
||||
$sql = $settings->get('sql');
|
||||
if (is_array($sql)) {
|
||||
$res = $this->installSQL($sql, $version_new, $version_old, $name);
|
||||
if ($res) {
|
||||
$result[] = $res;
|
||||
if($res){
|
||||
$result[]=$res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление версии меню
|
||||
$registry->removeKey($name);
|
||||
$registry->set($name, [
|
||||
'version' => $version_new,
|
||||
'time' => filemtime($setup)
|
||||
]);
|
||||
// $registry->writeKey([$name], $settings->export());
|
||||
// Обновление версии меню
|
||||
$registry->removeKey($name);
|
||||
$registry->writeKey(array($name), $settings->get('settings'));
|
||||
$registry->writeKey(array($name),
|
||||
array('version' => $version_new,
|
||||
'time' => filemtime($setup)));
|
||||
}
|
||||
|
||||
$registry->write();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает базу данных
|
||||
* @param string $dbinit_path
|
||||
* @param string|null $dbfill_path
|
||||
*/
|
||||
function install($dbinit_path, $dbfill_path = null): void
|
||||
{
|
||||
$json_installer = new JsonInstall($this->db_manager);
|
||||
function install($dbinit_path, $dbfill_path = null) {
|
||||
$json_installer = new Database_JsonInstall($this->db_manager);
|
||||
$json_installer->install($dbinit_path, $dbfill_path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Controller;
|
||||
use ctiso\HttpRequest;
|
||||
|
||||
class Request {
|
||||
/** @var HttpRequest */
|
||||
class Controller_Request {
|
||||
public $r;
|
||||
/** @var string */
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @param HttpRequest $request
|
||||
* @param string $id
|
||||
*/
|
||||
function __construct($request, $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;
|
||||
if ($id && is_array($v)) {
|
||||
return $v[$id] ?? $def;
|
||||
return isset($v[$id]) ? $v[$id] : $def;
|
||||
}
|
||||
return $v;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,101 +3,72 @@
|
|||
/**
|
||||
* Класс сервиса = Упрощенный компонент
|
||||
*/
|
||||
namespace ctiso\Controller;
|
||||
|
||||
use ctiso\Path;
|
||||
use ctiso\Model\BaseMapper;
|
||||
use ctiso\File;
|
||||
use ctiso\Registry;
|
||||
use ctiso\Database\PDOStatement;
|
||||
use ctiso\Database;
|
||||
|
||||
class Service
|
||||
class Controller_Service
|
||||
{
|
||||
/** @var array */
|
||||
public $viewPath = [];
|
||||
/** @var array */
|
||||
public $webPath = [];
|
||||
/** @var Registry */
|
||||
public $config;
|
||||
/** @var string */
|
||||
public $registry; // Registry->getInstance
|
||||
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 class-string $modelName
|
||||
* @return BaseMapper
|
||||
* @param $name Имя модели
|
||||
*/
|
||||
public function getModel($modelName)
|
||||
private function getModelPath($name)
|
||||
{
|
||||
/** @var BaseMapper */
|
||||
$model = new $modelName();
|
||||
return Path::join (CMS_PATH, "model", $name . ".php");
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает модель
|
||||
* @param string $name
|
||||
* @return model
|
||||
*/
|
||||
public function getModel($name)
|
||||
{
|
||||
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) {
|
||||
$result = [];
|
||||
$result = array();
|
||||
while($res->next()) {
|
||||
$result[] = ['value' => $res->getInt($key), 'name' => $res->getString($val)];
|
||||
$result[] = array('value' => $res->getInt($key), 'name' => $res->getString($val));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $list
|
||||
* @param bool $selected
|
||||
* @return array
|
||||
*/
|
||||
public function optionsPair($list, $selected = false) {
|
||||
$result = [];
|
||||
$result = array();
|
||||
foreach ($list as $key => $value) {
|
||||
$result [] = ['value' => $key, 'name' => $value, 'selected' => $key == $selected];
|
||||
$result [] = array('value' => $key, 'name' => $value, 'selected' => $key == $selected);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
||||
function getInfo() {
|
||||
$filename = Path::join($this->viewPath[0], 'install.json');
|
||||
if (file_exists($filename)) {
|
||||
$settings = json_decode(File::getContents($filename), true);
|
||||
return $settings;
|
||||
}
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Controller;
|
||||
|
||||
interface SiteInterface {
|
||||
/**
|
||||
* FIXME: Возвращает ресурс (но его тип опрделяется в App)
|
||||
* @return mixed
|
||||
*/
|
||||
function getResource();
|
||||
/**
|
||||
* @return \ctiso\Database
|
||||
*/
|
||||
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;
|
||||
}
|
||||
70
src/Controller/State.php
Normal file
70
src/Controller/State.php
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
class Controller_State
|
||||
{
|
||||
public $action = '';
|
||||
public $states = array();
|
||||
public $titles = array();
|
||||
|
||||
public function __construct($action)
|
||||
{
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
static function make($action)
|
||||
{
|
||||
return new Controller_State($action);
|
||||
}
|
||||
|
||||
public function addTitle($name, $url = array())
|
||||
{
|
||||
$this->titles [] = array($name, $url);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addState(Controller_State $state)
|
||||
{
|
||||
$this->states [$state->getAction()] = $state;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAction()
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
function checkAction($action, &$list)
|
||||
{
|
||||
if ($this->action == $action) {
|
||||
array_push($list, $this);
|
||||
return true;
|
||||
} else {
|
||||
foreach ($this->states as $state) {
|
||||
if ($state->checkAction($action, $list)) {
|
||||
array_push($list, $this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function makeTitle(Controller_Action $module)
|
||||
{
|
||||
foreach ($this->titles as $item) {
|
||||
$module->path->addMenuItem($module->nUrl($this->action, $item[1]), $item[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function getPath($module, $action)
|
||||
{
|
||||
$list = array();
|
||||
if ($this->checkAction($action, $list)) {
|
||||
foreach (array_reverse($list) as $item) {
|
||||
$item->makeTitle($module);
|
||||
}
|
||||
} else {
|
||||
$this->makeTitle($module);
|
||||
}
|
||||
}
|
||||
}
|
||||
414
src/Database.php
414
src/Database.php
|
|
@ -1,280 +1,190 @@
|
|||
<?php
|
||||
namespace {
|
||||
if (!function_exists('sqliteLower')) {
|
||||
/**
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
function sqliteLower($str)
|
||||
{
|
||||
return mb_strtolower($str, 'UTF-8');
|
||||
}
|
||||
///<reference path="Database/PDOStatement.php" />
|
||||
require_once "Database/PDOStatement.php";
|
||||
|
||||
/**
|
||||
* Класс оболочка для PDO для замены Creole
|
||||
*/
|
||||
class Database/*<Database_PDOStatement>*/ extends PDO
|
||||
{
|
||||
|
||||
public $dsn;
|
||||
public function __construct($dsn, $username = null, $password = null)
|
||||
{
|
||||
parent::__construct($dsn, $username, $password);
|
||||
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Database_PDOStatement', array()));
|
||||
}
|
||||
|
||||
function prepare($sql, $args = []) {
|
||||
$result = parent::prepare($sql, $args);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ctiso {
|
||||
|
||||
use PDO;
|
||||
use ctiso\Database\Statement;
|
||||
use ctiso\Database\PDOStatement;
|
||||
use ctiso\Database\IdGenerator;
|
||||
|
||||
public function getDSN()
|
||||
{
|
||||
return $this->dsn;
|
||||
}
|
||||
public function isPostgres(){
|
||||
return ($this->dsn["phptype"] == "pgsql");
|
||||
}
|
||||
/**
|
||||
* Класс оболочка для PDO для замены Creole
|
||||
* @phpstan-type DSN = array{phptype: string, hostspec: string, database: string, username: string, password: string}
|
||||
* Создает соединение с базой данных
|
||||
*/
|
||||
class Database extends PDO
|
||||
static function getConnection(array $dsn)
|
||||
{
|
||||
|
||||
/** @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);
|
||||
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [PDOStatement::class, []]);
|
||||
if ($dsn['phptype'] == 'pgsql' || $dsn['phptype'] == 'mysql') {
|
||||
$port = (isset($dsn['port'])) ? "port={$dsn['port']};" : "";
|
||||
$connection/*: Database*/ = new static("{$dsn['phptype']}:host={$dsn['hostspec']}; $port dbname={$dsn['database']}", $dsn['username'], $dsn['password']);
|
||||
if ($dsn['phptype'] == 'pgsql') {
|
||||
$connection->query('SET client_encoding="UTF-8"');
|
||||
}
|
||||
|
||||
if (isset($dsn['schema'])) {
|
||||
$connection->query('SET search_path TO ' . $dsn['schema']);
|
||||
}
|
||||
}
|
||||
if ($dsn['phptype'] == 'sqlite') {
|
||||
$connection/*: Database*/ = new static("{$dsn['phptype']}:{$dsn['database']}");
|
||||
$connection->setAttribute(PDO::ATTR_TIMEOUT, 5);
|
||||
$mode = defined('SQLITE_JOURNAL_MODE') ? SQLITE_JOURNAL_MODE : 'WAL';
|
||||
$connection->query("PRAGMA journal_mode=$mode");
|
||||
|
||||
/**
|
||||
* prepare возвращает только PDOStatement т.к установлен PDO::ERRMODE_EXCEPTION
|
||||
*/
|
||||
function prepare(string $sql, array $options = []): PDOStatement
|
||||
{
|
||||
/** @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']);
|
||||
if ($dsn['phptype'] == 'pgsql') {
|
||||
$connection->query('SET client_encoding="UTF-8"');
|
||||
if(!function_exists('sqliteLower')){
|
||||
function sqliteLower($str) {
|
||||
return mb_strtolower($str, 'UTF-8');
|
||||
}
|
||||
|
||||
if (isset($dsn['schema'])) {
|
||||
$connection->query('SET search_path TO ' . $dsn['schema']);
|
||||
}
|
||||
} elseif ($dsn['phptype'] == 'sqlite::memory') {
|
||||
$connection = new self("{$dsn['phptype']}:");
|
||||
$connection->sqliteCreateFunction('LOWER', 'sqliteLower', 1);
|
||||
} elseif ($dsn['phptype'] == 'sqlite') {
|
||||
$connection = new self("{$dsn['phptype']}:{$dsn['database']}");
|
||||
$connection->setAttribute(PDO::ATTR_TIMEOUT, 5);
|
||||
$mode = defined('SQLITE_JOURNAL_MODE') ? \SQLITE_JOURNAL_MODE : 'WAL';
|
||||
$connection->query("PRAGMA journal_mode=$mode");
|
||||
$connection->sqliteCreateFunction('LOWER', 'sqliteLower', 1);
|
||||
}
|
||||
$connection->dsn = $dsn;
|
||||
return $connection;
|
||||
}
|
||||
$connection->dsn = $dsn;
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет запрос к базе данных
|
||||
* @param string $query - запрос
|
||||
* @param ?array<string, mixed> $values - значения
|
||||
*/
|
||||
public function executeQuery($query, $values = null): PDOStatement
|
||||
{
|
||||
$stmt = $this->prepare($query);
|
||||
public function executeQuery($query, $values=null)
|
||||
{
|
||||
$stmt/*: Database_PDOStatement*/ = $this->prepare($query);
|
||||
|
||||
$stmt->execute($values);
|
||||
$stmt->cache = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $stmt;
|
||||
}
|
||||
$stmt->execute($values);
|
||||
$stmt->cache = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает подготовленный запрос
|
||||
* @param string $query - запрос
|
||||
* @return Statement
|
||||
*/
|
||||
public function prepareStatement($query)
|
||||
{
|
||||
return new Statement($query, $this);
|
||||
}
|
||||
public function prepareStatement($query)
|
||||
{
|
||||
return new Database_Statement($query, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Извлекает из базы все элементы по запросу (Для совместимости со старым представлением баз данных CIS)
|
||||
* @param string $query - запрос
|
||||
* @param ?array<string, mixed> $values - значения
|
||||
* @return array<array<string, mixed>>
|
||||
*/
|
||||
public function fetchAllArray($query, $values = null)
|
||||
{
|
||||
$sth = $this->prepare($query);
|
||||
$prep = $this->prepareValues($values);
|
||||
$sth->execute($prep);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
// Для совместимости со старым представлением баз данных CIS
|
||||
/**
|
||||
* Извлекает из базы все элементы по запросу
|
||||
*/
|
||||
public function fetchAllArray($query, $values = null)
|
||||
{
|
||||
$sth/*: Database_PDOStatement*/ = $this->prepare($query);
|
||||
$prep = $this->prepareValues($values);
|
||||
$sth->execute($prep);
|
||||
return $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Извлекает из базы первый элемент по запросу
|
||||
* @param string $query - запрос
|
||||
* @param ?array<string, mixed> $values - значения
|
||||
* @return array<string, mixed>|false
|
||||
*/
|
||||
public function fetchOneArray($query, $values = null)
|
||||
{
|
||||
$sth = $this->prepare($query);
|
||||
$prep = $this->prepareValues($values);
|
||||
$sth->execute($prep);
|
||||
return $sth->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
/**
|
||||
* Извлекает из базы первый элемент по запросу
|
||||
*/
|
||||
public function fetchOneArray($query, $values = null)
|
||||
{
|
||||
$sth/*: Database_PDOStatement*/ = $this->prepare($query);
|
||||
$prep = $this->prepareValues($values);
|
||||
$sth->execute($prep);
|
||||
return $sth->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Преобразует значения в подготовленные значения
|
||||
* @param array $values - значения
|
||||
* @return ?array<string, string>
|
||||
*/
|
||||
private function prepareValues($values)
|
||||
{
|
||||
if (!$values) {
|
||||
return null;
|
||||
}
|
||||
$pg = $this->isPostgres();
|
||||
$prep = [];
|
||||
foreach ($values as $key => $value) {
|
||||
$result = null;
|
||||
if (is_bool($value)) {
|
||||
if ($pg) {
|
||||
$result = $value ? 'true' : 'false';
|
||||
} else {
|
||||
$result = $value ? 1 : 0;
|
||||
}
|
||||
} else {
|
||||
$result = $value;
|
||||
}
|
||||
$prep[":" . $key] = $result;
|
||||
}
|
||||
return $prep;
|
||||
}
|
||||
private static function assignQuote($x, $y)
|
||||
{
|
||||
return $x . "=" . $this->quote($y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает 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)
|
||||
{
|
||||
$prep = $this->prepareValues($values);
|
||||
|
||||
$sql = "INSERT INTO $table (" . implode(",", array_keys($values))
|
||||
. ") VALUES (" . implode(",", array_keys($prep)) . ")";
|
||||
if ($return_id) {
|
||||
if ($this->isPostgres()) {
|
||||
$sql .= " RETURNING $index";
|
||||
}
|
||||
}
|
||||
$stmt = $this->prepare($sql);
|
||||
$stmt->setFetchMode(PDO::FETCH_ASSOC);
|
||||
$stmt->execute($prep);
|
||||
$result = $stmt->fetch();
|
||||
if ($return_id) {
|
||||
if ($this->isPostgres()) {
|
||||
return $result[$index];
|
||||
} else {
|
||||
$result = $this->fetchOneArray("SELECT $index AS lastid FROM $table WHERE OID = last_insert_rowid()");
|
||||
if ($result === false) {
|
||||
throw new \RuntimeException("Ошибка получения идентификатора");
|
||||
}
|
||||
return $result['lastid'];
|
||||
}
|
||||
}
|
||||
private function prepareValues($values)
|
||||
{
|
||||
if (!$values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает UPDATE запрос
|
||||
* @param string $table - таблица
|
||||
* @param array $values - значения
|
||||
* @param string $cond - условие
|
||||
*/
|
||||
function updateQuery($table, array $values, $cond): void
|
||||
{
|
||||
$prep = $this->prepareValues($values);
|
||||
$sql = "UPDATE $table SET " . implode(
|
||||
",",
|
||||
array_map(function ($k, $v) {
|
||||
return $k . "=" . $v; }, array_keys($values), array_keys($prep))
|
||||
) . " WHERE $cond";
|
||||
|
||||
$stmt = $this->prepare($sql);
|
||||
$stmt->setFetchMode(PDO::FETCH_ASSOC);
|
||||
$stmt->execute($prep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает генератор идентификаторов
|
||||
* @return IdGenerator
|
||||
*/
|
||||
function getIdGenerator()
|
||||
{
|
||||
return new IdGenerator($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Замечание: Только для Postgres SQL
|
||||
* @param string $seq Имя последовательности для ключа таблицы
|
||||
* @return int Идентефикатор следующей записи
|
||||
*/
|
||||
function getNextId($seq)
|
||||
{
|
||||
$result = $this->fetchOneArray("SELECT nextval('$seq')");
|
||||
if ($result === false) {
|
||||
throw new \RuntimeException("Ошибка получения следующего идентификатора");
|
||||
$pg = $this->isPostgres();
|
||||
$prep = array();
|
||||
foreach ($values as $key => $value) {
|
||||
$result = null;
|
||||
if(is_bool($value)) {
|
||||
if ($pg) {
|
||||
$result = $value ? 'true' : 'false';
|
||||
} else {
|
||||
$result = $value ? 1 : 0;
|
||||
}
|
||||
} else {
|
||||
$result = $value;
|
||||
}
|
||||
return $result['nextval'];
|
||||
$prep[":" . $key] = $result;
|
||||
}
|
||||
return $prep;
|
||||
}
|
||||
/**
|
||||
* Создает INSERT запрос
|
||||
*/
|
||||
function insertQuery($table, array $values, $return_id = false, $index = null)
|
||||
{
|
||||
$prep = $this->prepareValues($values);
|
||||
|
||||
/**
|
||||
* Закрывает соединение с базой данных
|
||||
*/
|
||||
function close(): void
|
||||
{
|
||||
$sql = "INSERT INTO $table (" . implode(",", array_keys($values))
|
||||
. ") VALUES (" . implode(",", array_keys($prep)). ")";
|
||||
if ($return_id) {
|
||||
if ($this->isPostgres()){
|
||||
$sql = $sql." RETURNING $index";
|
||||
}
|
||||
}
|
||||
$stmt = $this->prepare($sql);
|
||||
$stmt->setFetchMode(PDO::FETCH_ASSOC);
|
||||
$stmt->execute($prep);
|
||||
$result = $stmt->fetch();
|
||||
if ($return_id) {
|
||||
if ($this->isPostgres()) {
|
||||
return $result[$index];
|
||||
} else {
|
||||
$result = $this->fetchOneArray("SELECT $index AS lastid FROM $table WHERE OID = last_insert_rowid()");
|
||||
return $result['lastid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает UPDATE запрос
|
||||
*/
|
||||
function updateQuery($table, array $values, $cond)
|
||||
{
|
||||
$prep = $this->prepareValues($values);
|
||||
$sql = "UPDATE $table SET " . implode(",",
|
||||
array_map(function($k,$v){return $k."=".$v;}, array_keys($values), array_keys($prep))) . " WHERE $cond";
|
||||
|
||||
$stmt = $this->prepare($sql);
|
||||
$stmt->setFetchMode(PDO::FETCH_ASSOC);
|
||||
$stmt->execute($prep);
|
||||
}
|
||||
|
||||
function getIdGenerator() {
|
||||
return new Database_IdGenerator($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Замечание: Только для Postgres SQL
|
||||
* @param string $seq Имя последовательности для ключа таблицы
|
||||
* @return int Идентефикатор следующей записи
|
||||
*/
|
||||
function getNextId($seq) {
|
||||
$result = $this->fetchOneArray("SELECT nextval('$seq')");
|
||||
return $result['nextval'];
|
||||
}
|
||||
|
||||
function close()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Database;
|
||||
use ctiso\Database;
|
||||
|
||||
class IdGenerator {
|
||||
/** @var Database */
|
||||
class Database_IdGenerator {
|
||||
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'];
|
||||
}
|
||||
return intval($result['nextval']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,175 +1,143 @@
|
|||
<?php
|
||||
//Действия с базой данных согласно json файлу.
|
||||
|
||||
namespace ctiso\Database;
|
||||
use ctiso\Database\Manager;
|
||||
|
||||
class JsonInstall {
|
||||
/** @var Manager */
|
||||
class Database_JsonInstall {
|
||||
public $db_manager;
|
||||
/** @var array */
|
||||
public $serialColumns;
|
||||
|
||||
public function __construct(Manager $db_manager) {
|
||||
$this->db_manager = $db_manager;
|
||||
}
|
||||
public function __construct(Database_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)) {
|
||||
$initActions = json_decode($dbinit_file, true);
|
||||
if (!$initActions) {
|
||||
echo "Invalid ".$dbinit_path;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
echo "No ".$dbinit_path;
|
||||
return 0;
|
||||
}
|
||||
function install($dbinit_path, $dbfill_path = null) {
|
||||
$dbinit_file = file_get_contents($dbinit_path);
|
||||
if (is_string($dbinit_file)) {
|
||||
$initActions = json_decode($dbinit_file, true);
|
||||
if (!$initActions) {
|
||||
echo "Invalid ".$dbinit_path;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
echo "No ".$dbinit_path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->initDataBase($initActions, $dbinit_path);
|
||||
if ($dbfill_path) {
|
||||
$this->fillDataBase($dbfill_path);
|
||||
}
|
||||
$this->makeConstraints($initActions);
|
||||
return 1;
|
||||
}
|
||||
$this->initDataBase($initActions, $dbinit_path);
|
||||
if ($dbfill_path) {
|
||||
$this->fillDataBase($dbfill_path);
|
||||
}
|
||||
$this->makeConstraints($initActions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить список таблиц, которые не существуют в базе данных
|
||||
* @param array $tables
|
||||
* @return array
|
||||
*/
|
||||
function missingTables($tables) {
|
||||
$actual_tables = $this->db_manager->getAllTableNames();
|
||||
$missingTables = [];
|
||||
foreach ($tables as $table) {
|
||||
if (!in_array($table, $actual_tables))
|
||||
$missingTables[] = $table;
|
||||
}
|
||||
return $missingTables;
|
||||
}
|
||||
function missingTables($tables) {
|
||||
$actual_tables = $this->db_manager->GetAllTableNames();
|
||||
$missingTables = [];
|
||||
foreach ($tables as $table) {
|
||||
if (!in_array($table, $actual_tables))
|
||||
$missingTables[] = $table;
|
||||
}
|
||||
return $missingTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать таблицы
|
||||
* @param array $initActions
|
||||
* @param string $dbinit_path
|
||||
* @return void
|
||||
*/
|
||||
function initDataBase(array $initActions, $dbinit_path) {
|
||||
$pg = $this->db_manager->db->isPostgres();
|
||||
if (!$pg) {
|
||||
$refs = [];
|
||||
//В sqlite нет alter reference. Референсы надо создавать при создании таблицы.
|
||||
foreach ($initActions as $action) {
|
||||
if ($action["type"] == "alterReference") {
|
||||
if (!isset($refs[$action["table"]]))
|
||||
$refs[$action["table"]] = [];
|
||||
$refs[$action["table"]][]=$action;//добавить к списку референсов для таблицы
|
||||
}
|
||||
}
|
||||
}
|
||||
//Создать таблицы
|
||||
function initDataBase($initActions/*: array*/, $dbinit_path) {
|
||||
$pg = $this->db_manager->db->isPostgres();
|
||||
if (!$pg) {
|
||||
$refs = [];
|
||||
//В sqlite нет alter reference. Референсы надо создавать при создании таблицы.
|
||||
foreach ($initActions as $action) {
|
||||
if ($action["type"] == "alterReference") {
|
||||
if (!isset($refs[$action["table"]]))
|
||||
$refs[$action["table"]] = [];
|
||||
$refs[$action["table"]][]=$action;//добавить к списку референсов для таблицы
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($initActions as $action) {
|
||||
if (!$pg) {
|
||||
if ($action["type"] == "createTable") {
|
||||
$table_name = $action["table_name"];
|
||||
if (isset($refs[$table_name])) {
|
||||
foreach ($refs[$table_name] as $value) {
|
||||
$action['fields'][$value['column']]['references'] = [
|
||||
"refTable" => $value['refTable'],
|
||||
'refColumn' => $value['refColumn']
|
||||
];
|
||||
}
|
||||
}
|
||||
foreach ($initActions as $action) {
|
||||
if (!$pg) {
|
||||
if ($action["type"] == "createTable") {
|
||||
$table_name = $action["table_name"];
|
||||
if (isset($refs[$table_name])) {
|
||||
foreach ($refs[$table_name] as $value) {
|
||||
$action['fields'][$value['column']]['references'] =
|
||||
$value['refTable']."(".$value['refColumn'].")";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if ($action["type"] != "alterReference") {
|
||||
$this->db_manager->executeAction($action, $dbinit_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($action["type"] != "alterReference") {
|
||||
$this->db_manager->ExecuteAction($action, $dbinit_path);
|
||||
}
|
||||
}
|
||||
|
||||
//Запомнить все колонки serial
|
||||
$this->serialColumns = [];
|
||||
if ($pg) {
|
||||
foreach ($initActions as $action) {
|
||||
if ($action["type"] == "createTable") {
|
||||
foreach ($action["fields"] as $name => $field) {
|
||||
if ($field["type"]=="serial") {
|
||||
$this->serialColumns[] = [
|
||||
//Запомнить все колонки serial
|
||||
$this->serialColumns = [];
|
||||
if ($pg) {
|
||||
foreach ($initActions as $action) {
|
||||
if ($action["type"] == "createTable") {
|
||||
foreach ($action["fields"] as $name => $field) {
|
||||
if ($field["type"]=="serial") {
|
||||
$this->serialColumns[] = [
|
||||
"table"=>$action["table_name"],
|
||||
"column"=>$name
|
||||
"column"=>$name
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Заполнить данными
|
||||
* @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)) {
|
||||
$actions = json_decode($dbfill_file,true);
|
||||
if ($actions) {
|
||||
|
||||
//Проверка что упоминаемые в списке действий таблицы уже есть в базе
|
||||
$affected_tables = [];
|
||||
foreach ($actions as $action) {
|
||||
if ($action["table_name"]) {
|
||||
$affected_tables[$action["table_name"]] = 1;
|
||||
//Заполнить данными
|
||||
function fillDataBase($dbfill_file_path) {
|
||||
$dbfill_file = file_get_contents($dbfill_file_path);
|
||||
if (is_string($dbfill_file)) {
|
||||
$actions = json_decode($dbfill_file,true);
|
||||
if ($actions) {
|
||||
|
||||
//Проверка что упоминаемые в списке действий таблицы уже есть в базе
|
||||
$affected_tables = [];
|
||||
foreach ($actions as $action) {
|
||||
if ($action["table_name"]) {
|
||||
$affected_tables[$action["table_name"]] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$missing = $this->missingTables(array_keys($affected_tables));
|
||||
if (!empty($missing)) {
|
||||
echo "dbfill error. Missing tables: ".implode(" ", $missing);
|
||||
return;
|
||||
$missing = $this->missingTables(array_keys($affected_tables));
|
||||
if (!empty($missing)) {
|
||||
echo "dbfill error. Missing tables: ".implode(" ", $missing);
|
||||
return;
|
||||
}
|
||||
|
||||
//Выполнение действий
|
||||
foreach ($actions as $action) {
|
||||
$this->db_manager->ExecuteAction($action, $dbfill_file_path);
|
||||
}
|
||||
} else {
|
||||
echo "Invalid dbfill.json";
|
||||
}
|
||||
} else {
|
||||
echo "No dbfill.json";
|
||||
}
|
||||
}
|
||||
|
||||
//Обновить ключи serial и создать ограничения
|
||||
function makeConstraints($initActions) {
|
||||
$pg = $this->db_manager->db->isPostgres();
|
||||
if ($pg) {
|
||||
foreach ($this->serialColumns as $serialColumn) {
|
||||
$this->db_manager->UpdateSerial($serialColumn["table"], $serialColumn["column"]);
|
||||
}
|
||||
|
||||
|
||||
foreach ($initActions as $action) {
|
||||
if ($action["type"] == "alterReference") {
|
||||
$this->db_manager->ExecuteAction($action);
|
||||
}
|
||||
|
||||
//Выполнение действий
|
||||
foreach ($actions as $action) {
|
||||
$this->db_manager->executeAction($action, $dbfill_file_path);
|
||||
}
|
||||
} else {
|
||||
echo "Invalid dbfill.json";
|
||||
}
|
||||
} else {
|
||||
echo "No dbfill.json";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить ключи serial и создать ограничения
|
||||
* @param array $initActions
|
||||
* @return void
|
||||
*/
|
||||
function makeConstraints($initActions) {
|
||||
$pg = $this->db_manager->db->isPostgres();
|
||||
if ($pg) {
|
||||
foreach ($this->serialColumns as $serialColumn) {
|
||||
$this->db_manager->updateSerial($serialColumn["table"], $serialColumn["column"]);
|
||||
}
|
||||
|
||||
|
||||
foreach ($initActions as $action) {
|
||||
if ($action["type"] == "alterReference") {
|
||||
$this->db_manager->executeAction($action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,397 +1,221 @@
|
|||
<?php
|
||||
<?php
|
||||
|
||||
namespace ctiso\Database;
|
||||
|
||||
use ctiso\Database;
|
||||
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
|
||||
class Database_Manager
|
||||
{
|
||||
/** @var Database */
|
||||
public $db;
|
||||
public $db/*: Database*/;
|
||||
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
function __construct(Database $db) {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет действие
|
||||
* @param Action $action
|
||||
* @param string $db_file
|
||||
* @throws Exception
|
||||
*/
|
||||
public function executeAction(array $action, $db_file = ""): void
|
||||
{
|
||||
switch ($action["type"]) {
|
||||
case "dropTable":
|
||||
$this->dropTableQuery($action["table_name"], true);
|
||||
break;
|
||||
case "createTable":
|
||||
$constraints = $action["constraints"] ?? null;
|
||||
$this->createTableQuery($action["table_name"], $action["fields"], $constraints);
|
||||
break;
|
||||
public function ExecuteAction($action/*: array*/, $db_file = "") {
|
||||
switch($action["type"]) {
|
||||
case "dropTable":
|
||||
$this->DropTableQuery($action["table_name"], true);
|
||||
break;
|
||||
case "createTable":
|
||||
$constraints = isset($action["constraints"]) ? $action["constraints"] : NULL;
|
||||
$this->CreateTableQuery($action["table_name"], $action["fields"], $constraints);
|
||||
break;
|
||||
case "addColumn":
|
||||
$this->addColumn($action["table_name"], $action["column_name"], $action["field"]);
|
||||
$this->AddColumn($action["table_name"], $action["column_name"], $action["field"]);
|
||||
break;
|
||||
case "insert":
|
||||
$this->db->insertQuery($action["table_name"], $action["values"]);
|
||||
break;
|
||||
case "alterReference":
|
||||
$this->AlterReference($action["table"], $action["column"], $action["refTable"], $action["refColumn"]);
|
||||
break;
|
||||
case "renameColumn":
|
||||
$this->RenameColumn($action["table"], $action["old_name"], $action["new_name"]);
|
||||
break;
|
||||
case "insert":
|
||||
$this->db->insertQuery($action["table_name"], $action["values"]);
|
||||
break;
|
||||
case "alterReference":
|
||||
$this->alterReference($action["table"], $action["column"], $action["refTable"], $action["refColumn"]);
|
||||
break;
|
||||
case "renameColumn":
|
||||
$this->renameColumn($action["table"], $action["old_name"], $action["new_name"]);
|
||||
break;
|
||||
case "createView":
|
||||
$this->recreateView($action["view"], $action["select"]);
|
||||
break;
|
||||
case "executeFile":
|
||||
case "executeFile":
|
||||
if ($this->db->isPostgres() && isset($action["pgsql"])) {
|
||||
$file = $action["pgsql"];
|
||||
$file = $action["pgsql"];
|
||||
} else {
|
||||
$file = $action["source"];
|
||||
$file = $action["source"];
|
||||
}
|
||||
|
||||
$stmtList = SQLStatementExtractor::extractFile(Path::join(dirname($db_file), $file));
|
||||
foreach ($stmtList as $stmt) {
|
||||
$stmtList = Tools_SQLStatementExtractor::extractFile(Path::join(dirname($db_file), $file));
|
||||
foreach($stmtList as $stmt) {
|
||||
$this->db->executeQuery($stmt);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new Exception("unknown action " . $action["type"] . PHP_EOL);
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new Exception("unknown action ". $action["type"] . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Дропает и создаёт SQL VIEW
|
||||
* @param string $viewName
|
||||
* @param string $selectStatement
|
||||
*/
|
||||
public function recreateView($viewName, $selectStatement): void
|
||||
{
|
||||
$this->db->query("DROP VIEW " . $viewName);
|
||||
$this->db->query("CREATE VIEW " . $viewName . " AS " . $selectStatement);
|
||||
}
|
||||
public function DropTableQuery($table, $cascade=false) {
|
||||
$statement = "DROP TABLE IF EXISTS ".$table;
|
||||
if ($this->db->isPostgres()&&$cascade) {
|
||||
$statement = $statement." CASCADE";
|
||||
}
|
||||
$this->db->query($statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Дропает таблицу
|
||||
* @param string $table
|
||||
* @param bool $cascade
|
||||
*/
|
||||
public function dropTableQuery($table, $cascade = false): void
|
||||
{
|
||||
$statement = "DROP TABLE IF EXISTS " . $table;
|
||||
if ($this->db->isPostgres() && $cascade) {
|
||||
$statement .= " CASCADE";
|
||||
}
|
||||
$this->db->query($statement);
|
||||
}
|
||||
public function AlterReference($table,$column,$refTable,$refColumn) {
|
||||
$this->db->query("ALTER TABLE ".$table." ADD CONSTRAINT ".$table."_".$column."fk"." FOREIGN KEY (".$column.") REFERENCES ".$refTable." (".$refColumn.")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет ссылку на другую таблицу
|
||||
* @param string $table
|
||||
* @param string $column
|
||||
* @param string $refTable
|
||||
* @param string $refColumn
|
||||
*/
|
||||
public function alterReference($table, $column, $refTable, $refColumn): void
|
||||
{
|
||||
$this->db->query("ALTER TABLE " . $table . " ADD CONSTRAINT " . $table . "_" . $column . "fk" . " FOREIGN KEY (" . $column . ") REFERENCES " . $refTable . " (" . $refColumn . ") ON DELETE CASCADE ON UPDATE CASCADE");
|
||||
}
|
||||
|
||||
/**
|
||||
* Извлечение информации о полях таблицы
|
||||
* @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 . ");");
|
||||
if (empty($results)) {
|
||||
return 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.");");
|
||||
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
|
||||
$fields = [];
|
||||
foreach ($results as $result) {
|
||||
$fields[$result["name"]] = [
|
||||
"type"=> $result["type"],
|
||||
"not_null"=> boolval($result["notnull"]),
|
||||
"constraint"=> ((boolean) $result["pk"]) ? "PRIMARY KEY" : null
|
||||
];
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Переименование столбца в таблице
|
||||
* @param string $table
|
||||
* @param string $old_name
|
||||
* @param string $new_name
|
||||
*/
|
||||
public function renameColumn($table, $old_name, $new_name): void
|
||||
{
|
||||
$pg = $this->db->isPostgres();
|
||||
if ($pg) {
|
||||
$this->db->query("ALTER TABLE " . $table . " RENAME COLUMN " . $old_name . " TO " . $new_name);
|
||||
} else {
|
||||
$tmp_table = "tmp_" . $table;
|
||||
$this->dropTableQuery($tmp_table);
|
||||
$table_info = $this->tableInfo($table);
|
||||
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);
|
||||
} else {
|
||||
$tmp_table = "tmp_" . $table;
|
||||
$this->DropTableQuery($tmp_table);
|
||||
$table_info = $this->TableInfo($table);
|
||||
|
||||
if (isset($table_info[$new_name])) {
|
||||
return;
|
||||
if (isset($table_info[$new_name])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->dumpTable($table);
|
||||
$data/*: array*/ = $this->DumpTable($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);
|
||||
$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);
|
||||
|
||||
foreach ($data as $row) {
|
||||
$values = $row['values'];
|
||||
$values[$new_name] = $values[$old_name];
|
||||
unset($values[$old_name]);
|
||||
$this->db->insertQuery($table, $values);
|
||||
}
|
||||
$this->dropTableQuery($tmp_table);
|
||||
}
|
||||
}
|
||||
foreach ($data as $row) {
|
||||
$values = $row['values'];
|
||||
$values[$new_name] = $values[$old_name];
|
||||
unset($values[$old_name]);
|
||||
$this->db->insertQuery($table, $values);
|
||||
}
|
||||
$this->DropTableQuery($tmp_table);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновление ключа serial после ручной вставки
|
||||
* @param string $table
|
||||
* @param string $column
|
||||
*/
|
||||
public function updateSerial($table, $column): void
|
||||
{
|
||||
$this->db->query("SELECT setval(pg_get_serial_sequence('" . $table . "', '" . $column . "'), coalesce(max(" . $column . "),0) + 1, false) FROM " . $table);
|
||||
}
|
||||
//Обновление ключа 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает определение столбца
|
||||
* @param string $name
|
||||
* @param ColumnProps $data
|
||||
* @param bool $pg
|
||||
* @return string
|
||||
*/
|
||||
public function columnDefinition($name, $data, $pg)
|
||||
{
|
||||
$constraint = isset($data['constraint']) ? " " . $data['constraint'] : "";
|
||||
$references = "";
|
||||
if (isset($data['references'])) {
|
||||
$references = " REFERENCES " . $data['references']['refTable'] . '(' . $data['references']['refColumn'] . ')';
|
||||
}
|
||||
if (isset($data["not_null"]) && $data["not_null"]) {
|
||||
$constraint .= " NOT NULL";
|
||||
}
|
||||
$type = $data['type'];
|
||||
if (!$pg) {
|
||||
if (strtolower($type) == "serial") {
|
||||
$type = "integer";
|
||||
}
|
||||
}
|
||||
return $name . " " . $type . $references . $constraint;
|
||||
}
|
||||
public function Column_Definition($name,$data,$pg){
|
||||
$constraint = isset($data['constraint'])?" ".$data['constraint']:"";
|
||||
$references = "";
|
||||
if (isset($data['references'])) {
|
||||
$references = " REFERENCES ".$data['references'];
|
||||
}
|
||||
if (isset($data["not_null"]) && $data["not_null"])
|
||||
$constraint .=" NOT NULL";
|
||||
$type = $data['type'];
|
||||
if (!$pg) {
|
||||
if (strtolower($type)=="serial")
|
||||
$type = "integer";
|
||||
//if (strtolower($type)=="boolean")
|
||||
// $type = "integer";
|
||||
}
|
||||
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
|
||||
{
|
||||
$pg = $this->db->isPostgres();
|
||||
$q = "ALTER TABLE " . $table_name . " ADD COLUMN " .
|
||||
$this->columnDefinition($column_name, $field, $pg);
|
||||
$this->db->query($q);
|
||||
}
|
||||
public function AddColumn($table_name,$column_name,$field){
|
||||
$pg = $this->db->isPostgres();
|
||||
$q = "ALTER TABLE ".$table_name." ADD COLUMN ".
|
||||
$this->Column_Definition($column_name, $field, $pg);
|
||||
$this->db->query($q);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает определение ограничения
|
||||
* @param array{fields: string[], type: string} $c
|
||||
* @return string
|
||||
*/
|
||||
public function getConstraintDef(array $c)
|
||||
{
|
||||
function getConstraintDef($c/*: array*/) {
|
||||
if ($c['type'] == 'unique') {
|
||||
return "UNIQUE(" . implode(", ", $c['fields']) . ")";
|
||||
}
|
||||
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
|
||||
{
|
||||
$pg = $this->db->isPostgres();
|
||||
if ($constraints) {
|
||||
//CreateTableQuery('users',['id'=>['type'=>'integer','constraint'=>'PRIMARY KEY']])
|
||||
public function CreateTableQuery($table, $fields, $constraints) {
|
||||
$pg = $this->db->isPostgres();
|
||||
if ($constraints) {
|
||||
if (is_array($constraints)) {
|
||||
$constraints = $this->getConstraintDef($constraints);
|
||||
}
|
||||
$constraints = ", " . $constraints;
|
||||
$constraints = ", " . $constraints;
|
||||
}
|
||||
|
||||
$statement = "CREATE TABLE $table (" . implode(
|
||||
",",
|
||||
array_map(function ($name, $data) use ($pg) {
|
||||
return $this->columnDefinition($name, $data, $pg);
|
||||
}, array_keys($fields), array_values($fields))
|
||||
) . " " . $constraints . ")";
|
||||
$this->db->query($statement);
|
||||
}
|
||||
$statement = "CREATE TABLE $table (" . implode(",",
|
||||
array_map(function($name,$data) use ($pg) {
|
||||
return $this->Column_Definition($name,$data,$pg);
|
||||
}, array_keys($fields), array_values($fields))
|
||||
) . " " . $constraints . ")";
|
||||
$this->db->query($statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает дамп таблицы
|
||||
* @param string $table_name
|
||||
* @return array
|
||||
*/
|
||||
public function dumpTable($table_name)
|
||||
{
|
||||
$pg = $this->db->isPostgres();
|
||||
public function DumpTable($table_name) {
|
||||
$pg = $this->db->isPostgres();
|
||||
|
||||
$result = [];
|
||||
$data = $this->db->fetchAllArray("SELECT * FROM " . $table_name . ";");
|
||||
$result/*: array*/ = array();
|
||||
$data/*: array*/ = $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) {
|
||||
if (isset($row[$name])) {
|
||||
$row[$name] = boolval($row[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($data as $r) {
|
||||
$result[] = [
|
||||
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) {
|
||||
$row/*: array*/ = $row;
|
||||
if (isset($row[$name])) {
|
||||
$row[$name] = boolval($row[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($data as $r) {
|
||||
$result[] = array(
|
||||
"type" => "insert",
|
||||
"table_name" => $table_name,
|
||||
"values" => $r
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
"table_name" => $table_name,
|
||||
"values" => $r
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает все имена таблиц
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getAllTableNames()
|
||||
{
|
||||
$result = [];
|
||||
if ($this->db->isPostgres()) {
|
||||
$query = "SELECT table_name as name FROM information_schema.tables WHERE table_schema='public'";
|
||||
} else {
|
||||
$query = "SELECT * FROM sqlite_master WHERE type='table'";
|
||||
public function GetAllTableNames() {
|
||||
$result = [];
|
||||
if ($this->db->isPostgres()) {
|
||||
$query = "SELECT table_name as name FROM information_schema.tables WHERE table_schema='public'";
|
||||
} else {
|
||||
$query = "SELECT * FROM sqlite_master WHERE type='table'";
|
||||
}
|
||||
$tables = $this->db->fetchAllArray($query);
|
||||
foreach ($tables as $table) {
|
||||
$result[] = $table['name'];
|
||||
$tables = $this->db->fetchAllArray($query);
|
||||
foreach ($tables as $table) {
|
||||
$result[] = $table['name'];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает дамп всех таблиц
|
||||
* @return array
|
||||
*/
|
||||
public function dumpInserts()
|
||||
{
|
||||
$table_names = $this->getAllTableNames();
|
||||
$result = [];
|
||||
foreach ($table_names as $table_name) {
|
||||
$result = array_merge($result, $this->dumpTable($table_name));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
public function DumpInserts() {
|
||||
$table_names = $this->GetAllTableNames();
|
||||
$result = array();
|
||||
foreach ($table_names as $table_name) {
|
||||
$result = array_merge($result, $this->DumpTable($table_name));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Database;
|
||||
|
||||
use ctiso\Database\StatementIterator,
|
||||
ctiso\Tools\StringUtil,
|
||||
PDO;
|
||||
use TheSeer\Tokenizer\Exception;
|
||||
|
||||
/**
|
||||
* @implements \IteratorAggregate<int, array>
|
||||
*/
|
||||
class PDOStatement extends \PDOStatement implements \IteratorAggregate
|
||||
class Database_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 {
|
||||
return new StatementIterator($this);
|
||||
function getIterator(): Iterator {
|
||||
return new Database_StatementIterator($this);
|
||||
}
|
||||
|
||||
protected function __construct() {
|
||||
protected function __construct() {
|
||||
}
|
||||
|
||||
function rewind(): void {
|
||||
function rewind() {
|
||||
$this->cursorPos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $rownum
|
||||
* @return bool
|
||||
*/
|
||||
public function seek($rownum): bool {
|
||||
if ($rownum < 0) {
|
||||
return false;
|
||||
}
|
||||
public function seek($rownum) {
|
||||
if ($rownum < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// PostgreSQL rows start w/ 0, but this works, because we are
|
||||
// looking to move the position _before_ the next desired position
|
||||
$this->cursorPos = $rownum;
|
||||
return true;
|
||||
}
|
||||
|
||||
// PostgreSQL rows start w/ 0, but this works, because we are
|
||||
// looking to move the position _before_ the next desired position
|
||||
$this->cursorPos = $rownum;
|
||||
return true;
|
||||
function valid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function valid(): bool {
|
||||
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);
|
||||
}
|
||||
}
|
||||
$this->fields = $this->cache[$this->cursorPos];
|
||||
|
||||
$this->cursorPos++;
|
||||
|
|
@ -72,87 +53,50 @@ 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');
|
||||
throw new Error('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;
|
||||
return isset($this->fields[$name]) ? $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 Tools_String::strToArray($this->fields[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
function getRecordCount() {
|
||||
return count($this->cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
* @return bool
|
||||
*/
|
||||
function execute($args = null): bool {
|
||||
|
||||
function execute($args = null) {
|
||||
$result = parent::execute($args);
|
||||
return $result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,83 +1,47 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Класс оболочка для PDOStatement для замены Creole
|
||||
* Класс оболочка для PDOStatement для замены Creole
|
||||
*/
|
||||
namespace ctiso\Database;
|
||||
|
||||
use PDO;
|
||||
use ctiso\Database;
|
||||
|
||||
class Statement
|
||||
class Database_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 {
|
||||
$this->binds [] = [$n, $value, PDO::PARAM_INT];
|
||||
|
||||
function setInt($n, $value) {
|
||||
$this->binds [] = array($n, $value, PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $n
|
||||
* @param string $value
|
||||
*/
|
||||
function setString($n, $value): void {
|
||||
$this->binds [] = [$n, $value, PDO::PARAM_STR];
|
||||
function setString($n, $value) {
|
||||
$this->binds [] = array($n, $value, PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $n
|
||||
* @param mixed $value
|
||||
*/
|
||||
function setBlob($n, $value): void {
|
||||
$this->binds [] = [$n, $value, PDO::PARAM_LOB];
|
||||
function setBlob($n, $value) {
|
||||
$this->binds [] = array($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/*: Database_PDOStatement*/ = $this->conn->prepare($this->query);
|
||||
foreach ($this->binds as $bind) {
|
||||
list($n, $value, $type) = $bind;
|
||||
$stmt->bindValue($n, $value, (int) $type);
|
||||
|
|
|
|||
|
|
@ -1,59 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Database;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* @implements \Iterator<array>
|
||||
*/
|
||||
class StatementIterator implements \Iterator
|
||||
class Database_StatementIterator implements Iterator
|
||||
{
|
||||
/** @var PDOStatement */
|
||||
|
||||
private $result;
|
||||
/** @var int */
|
||||
private $pos = 0;
|
||||
/** @var int */
|
||||
private $fetchmode;
|
||||
private $row_count;
|
||||
|
||||
/**
|
||||
* @param PDOStatement $rs
|
||||
*/
|
||||
public function __construct($rs) {
|
||||
|
||||
public function __construct($rs/*: Database_PDOStatement*/) {
|
||||
$this->result = $rs;
|
||||
$this->row_count = $rs->getRecordCount();
|
||||
$this->row_count = $rs->getRecordCount();
|
||||
}
|
||||
|
||||
function rewind(): void{
|
||||
|
||||
function rewind() {
|
||||
$this->pos = 0;
|
||||
}
|
||||
|
||||
function valid(): bool {
|
||||
return ($this->pos < $this->row_count);
|
||||
|
||||
function valid() {
|
||||
return ($this->pos < $this->row_count);
|
||||
}
|
||||
|
||||
function key(): mixed {
|
||||
|
||||
function key() {
|
||||
return $this->pos;
|
||||
}
|
||||
|
||||
function current(): mixed{
|
||||
|
||||
function current() {
|
||||
if (!isset($this->result->cache[$this->pos])) {
|
||||
$this->result->cache[$this->pos] = $this->result->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
}
|
||||
return $this->result->cache[$this->pos];
|
||||
}
|
||||
|
||||
function next(): void {
|
||||
|
||||
function next() {
|
||||
$this->pos++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $index
|
||||
*/
|
||||
function seek($index): void {
|
||||
$this->pos = $index;
|
||||
function seek($index) {
|
||||
$this->pos = $index;
|
||||
}
|
||||
|
||||
function count(): int {
|
||||
return $this->row_count;
|
||||
function count() {
|
||||
return $this->row_count;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Excel;
|
||||
|
||||
class DateTime
|
||||
class Excel_DateTime
|
||||
{
|
||||
/** @var int */
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
function __construct($value)
|
||||
function __construct($value)
|
||||
{
|
||||
$this->value = (int)$value;
|
||||
$this->value = intval($value);
|
||||
}
|
||||
|
||||
function getString(): string
|
||||
function getString()
|
||||
{
|
||||
return date('Y-m-d\TH:i:s.u', $this->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,45 +3,33 @@
|
|||
/**
|
||||
* Документ
|
||||
*/
|
||||
namespace ctiso\Excel;
|
||||
use XMLWriter,
|
||||
Exception;
|
||||
|
||||
class Document {
|
||||
/** @var string */
|
||||
class Excel_Document {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление стиля к документу
|
||||
* @param string $name string Имя стиля
|
||||
* @param array $values array Параметры стиля
|
||||
* @param string $type Тип стиля
|
||||
* Добавление стиля к документу
|
||||
* @param $name string Имя стиля
|
||||
* @param $values array Параметры стиля
|
||||
* @param $type Тип стиля
|
||||
*/
|
||||
function setStyle ($name, array $values, $type = 'Interior'): void
|
||||
function setStyle ($name, array $values, $type = 'Interior')
|
||||
{
|
||||
if(!isset($this->styles[$name])) {
|
||||
$this->styles[$name] = [];
|
||||
}
|
||||
$this->styles[$name] = array();
|
||||
}
|
||||
$this->styles[$name][$type] = $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерация стилей
|
||||
* Генерация стилей
|
||||
*/
|
||||
private function createStyles (XMLWriter $doc): void
|
||||
{
|
||||
private function createStyles (XMLWriter $doc) {
|
||||
$doc->startElement('Styles');
|
||||
foreach ($this->styles as $name => $sn) {
|
||||
$doc->startElement('Style');
|
||||
|
|
@ -51,6 +39,7 @@ class Document {
|
|||
if ($type == 'Borders') {
|
||||
$doc->startElement('Borders');
|
||||
foreach ($s as $border) {
|
||||
$border/*: array*/ = $border;
|
||||
$doc->startElement('Border');
|
||||
foreach ($border as $key => $value) {
|
||||
$doc->writeAttribute('ss:' . $key, $value);
|
||||
|
|
@ -73,25 +62,21 @@ class Document {
|
|||
|
||||
/**
|
||||
* Преобразует переводы строки в спец символы
|
||||
* @param string $s
|
||||
* @return string
|
||||
*/
|
||||
function clean ($s) {
|
||||
return strtr($s, ["\n" => " "]);
|
||||
}
|
||||
assert(is_string($s));
|
||||
|
||||
/**
|
||||
return strtr($s, array ("\n" => " "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохраняет таблицу в формате 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)) {
|
||||
if (!$doc->openURI($filename)) {
|
||||
throw new Exception("unknown file " . $filename);
|
||||
}
|
||||
$doc->setIndent(false);
|
||||
|
|
@ -101,9 +86,9 @@ class Document {
|
|||
$doc->writeAttribute('xmlns:ss', self::$ns);
|
||||
|
||||
$this->createStyles($doc);
|
||||
|
||||
|
||||
foreach ($this->table as $table) {
|
||||
if ($table instanceof Table) {
|
||||
if ($table instanceof Excel_Table) {
|
||||
$table->createTable($doc);
|
||||
} else {
|
||||
$table_data = call_user_func($table);
|
||||
|
|
|
|||
|
|
@ -1,23 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Excel;
|
||||
|
||||
class Number
|
||||
class Excel_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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,14 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Клетка таблицы
|
||||
* Клетка таблицы
|
||||
*/
|
||||
namespace ctiso\Excel;
|
||||
use XMLWriter,
|
||||
ctiso\Excel\DateTime as Excel_DateTime,
|
||||
ctiso\Excel\Number as Excel_Number;
|
||||
|
||||
class TableCell
|
||||
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;
|
||||
|
|
@ -27,86 +16,65 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Таблица
|
||||
* Таблица
|
||||
*/
|
||||
class Table
|
||||
class Excel_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()
|
||||
{
|
||||
$this->name = "Page " . ((int)self::$index ++);
|
||||
$this->name = "Page " . intval(self::$index ++);
|
||||
}
|
||||
|
||||
/**
|
||||
* Записать значение в клетку с заданными координатами
|
||||
* @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,125 +85,115 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает стиль для клеток ряда
|
||||
* @param int $row Номер ряда
|
||||
* @param int $y Номер столбца
|
||||
* @param string $name Имя стиля
|
||||
* @param $row integer Номер ряда
|
||||
* @param $y integer Номер столбца
|
||||
* @parma $name string Имя стиля
|
||||
*/
|
||||
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 = array(""))
|
||||
{
|
||||
assert($index > 0);
|
||||
assert(is_numeric($index) && $index > 0);
|
||||
$offset = $this->getRows() + 1;
|
||||
|
||||
|
||||
$this->setRow($offset, $index, $data);
|
||||
return $offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Количество строк в таблице
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
*/
|
||||
function getRows()
|
||||
{
|
||||
// Высчитываем максимальный индекс, массив может быть разрежен поэтому используем array_keys
|
||||
$keys = array_keys($this->rows);
|
||||
return $keys === [] ? 0 : max($keys);
|
||||
$keys/*: array*/ = array_keys($this->rows);
|
||||
return max($keys);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Количество столбцов в строке
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
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 Количество столбцов слева
|
||||
* @param $n integer Количество столбцов слева
|
||||
*/
|
||||
function splitVertical($n): void {
|
||||
function splitVertical($n) {
|
||||
$this->_splitVertical = $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Разделяет таблицу на две части по горизонтали
|
||||
* @param int $n Количество столбцов сверху
|
||||
* @param $n integer Количество столбцов сверху
|
||||
*/
|
||||
function splitHorizontal($n): void {
|
||||
function splitHorizontal($n) {
|
||||
$this->_splitHorizontal = $n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Количество столбцов в таблице
|
||||
*
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
*/
|
||||
function getColumns() {
|
||||
$columns = array_map($this->getRowCells(...), $this->rows);
|
||||
return $columns === [] ? 0 : max($columns);
|
||||
return max(array_map(array($this, 'getRowCells'), $this->rows));
|
||||
}
|
||||
|
||||
/**
|
||||
* Кодирование строки
|
||||
* @deprecated Можно заменить на значение
|
||||
* @param string $s Строка
|
||||
* @return string
|
||||
*/
|
||||
function encode($s)
|
||||
{
|
||||
return $s;
|
||||
|
|
@ -243,13 +201,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 +210,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");
|
||||
|
|
@ -270,23 +223,23 @@ class Table
|
|||
$doc->text($value->getString());
|
||||
} else if ($value instanceof Excel_Number) {
|
||||
$doc->writeAttribute('ss:Type', "Number");
|
||||
$doc->text($value->getString());
|
||||
$doc->text($value->getString());
|
||||
} else {
|
||||
if (is_string($value)) {
|
||||
$doc->writeAttribute('ss:Type', "String");
|
||||
} else {
|
||||
$doc->writeAttribute('ss:Type', "Number");
|
||||
}
|
||||
$doc->writeCdata($value);
|
||||
}
|
||||
$doc->endElement();
|
||||
$doc->writeCData($this->encode($value));
|
||||
}
|
||||
$doc->endElement();
|
||||
$doc->endElement();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Генерация таблицы
|
||||
* Генерация таблицы
|
||||
*/
|
||||
public function createTable (XMLWriter $doc): void {
|
||||
public function createTable (XMLWriter $doc) {
|
||||
$doc->startElement('Worksheet');
|
||||
$doc->writeAttribute('ss:Name', $this->name);
|
||||
|
||||
|
|
@ -294,8 +247,8 @@ class Table
|
|||
$rows = $this->getRows();
|
||||
|
||||
$doc->startElement('Table');
|
||||
$doc->writeAttribute('ss:ExpandedColumnCount', (string)$columns);
|
||||
$doc->writeAttribute('ss:ExpandedRowCount', (string)$rows);
|
||||
$doc->writeAttribute('ss:ExpandedColumnCount', $columns);
|
||||
$doc->writeAttribute('ss:ExpandedRowCount', $rows);
|
||||
|
||||
// Переписать цыкл !!!!!!!
|
||||
for ($i = 1; $i <= $rows; $i++) {
|
||||
|
|
@ -306,26 +259,26 @@ 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++) {
|
||||
|
||||
|
||||
$value = null;
|
||||
if (isset($nrow->cells[$j])) {
|
||||
$value = $nrow->cells[$j]->value;
|
||||
}
|
||||
|
||||
|
||||
if (!empty($value)) {
|
||||
$this->createCell($nrow->cells[$j], $doc, $j, $value, $setIndex);
|
||||
$setIndex = false;
|
||||
} else {
|
||||
$setIndex = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$doc->endElement();
|
||||
}
|
||||
|
|
@ -334,24 +287,25 @@ class Table
|
|||
$doc->endElement();
|
||||
}
|
||||
|
||||
protected function splitPane (XMLWriter $doc): void {
|
||||
$doc->startElement('WorksheetOptions');
|
||||
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', 0);
|
||||
} else if($this->_splitHorizontal) {
|
||||
$doc->writeElement('ActivePane', (string) 2);
|
||||
$doc->writeElement('ActivePane', 2);
|
||||
}
|
||||
$doc->endElement();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
10
src/File.php
10
src/File.php
|
|
@ -1,16 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso;
|
||||
use Exception;
|
||||
|
||||
class File {
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
static function getContents($filename) {
|
||||
$buffer = @file_get_contents($filename);
|
||||
$buffer = file_get_contents($filename);
|
||||
if ($buffer !== false) {
|
||||
return $buffer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Фильтр действий
|
||||
* Фильтр действий
|
||||
*/
|
||||
namespace ctiso\Filter;
|
||||
|
||||
use ctiso\HttpRequest;
|
||||
use ctiso\Role\User;
|
||||
|
||||
class ActionAccess
|
||||
class Filter_ActionAccess
|
||||
{
|
||||
/** @var array */
|
||||
public $access = [];
|
||||
/** @var FilterInterface */
|
||||
public $access = array();
|
||||
public $processor;
|
||||
/** @var User */
|
||||
public $user;
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @param FilterInterface $processor
|
||||
* @param User $user
|
||||
*/
|
||||
function __construct($processor, $user) {
|
||||
function __construct($processor/*: Filter_Filter*/) {
|
||||
$this->processor = $processor;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка доступных действий для пользователя
|
||||
* !! Реализация класса проверки действий не должна быть внутри Контроллера!!!
|
||||
* Информация о доступе может быть в файле, базе данных и т.д.
|
||||
*
|
||||
* @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]));
|
||||
$name = $this->name;
|
||||
return (!isset($this->access[$name][$action]) || in_array(Filter_UserAccess::$access, $this->access[$name][$action]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HttpRequest $request
|
||||
* @return mixed
|
||||
*/
|
||||
function execute(HttpRequest $request) {
|
||||
$action = $request->getAction();
|
||||
if(! $this->checkAction($action)) {
|
||||
|
|
|
|||
|
|
@ -1,44 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Filter;
|
||||
use ctiso\Role\UserInterface,
|
||||
ctiso\HttpRequest;
|
||||
|
||||
/* Переделать формат Логов на список json */
|
||||
class ActionLogger implements FilterInterface
|
||||
{
|
||||
/** @var array */
|
||||
public $before = [];
|
||||
/** @var resource */
|
||||
class Filter_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_Filter*/) {
|
||||
$this->processor = $processor;
|
||||
$this->user = $user;
|
||||
|
||||
$file = fopen($logPath, "a");
|
||||
if (is_resource($file)) {
|
||||
$this->file = $file;
|
||||
} else {
|
||||
throw new \Exception('Ошибка открытия файла ' . $logPath);
|
||||
}
|
||||
$this->file = fopen(Shortcut::getUrl('access.log'), "a");
|
||||
}
|
||||
|
||||
function execute(HttpRequest $request) {
|
||||
$action = $request->getAction();
|
||||
if(in_array($action, $this->before)) {
|
||||
$line = ['time' => time(), 'user' => $this->user->getName(), 'sid' => session_id(), 'query' => array_merge($_POST, $_GET)];
|
||||
$line = ['time' => time(), 'user' => Filter_UserAccess::$name, 'sid' => session_id(), 'query' => array_merge($_POST, $_GET)];
|
||||
fwrite($this->file, json_encode($line) . "\n");
|
||||
}
|
||||
return $this->processor->execute($request);
|
||||
|
|
|
|||
|
|
@ -1,24 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Filter;
|
||||
|
||||
class Authorization {
|
||||
class Filter_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();
|
||||
|
|
@ -30,45 +16,37 @@ class Authorization {
|
|||
// UserAccess::getUserById($_SESSION ['access']); // Поиск по идентификатору
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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");
|
||||
|
||||
$_SESSION [$group] = $id;
|
||||
// $_SESSION ["group"] = $result->getInt('access');
|
||||
$_SESSION [$group] = $id; // id_user
|
||||
$_SESSION [self::SESSION_BROWSER_SIGN_KEYNAME] = self::getBrowserSign();
|
||||
$_SESSION ["sign"] = self::getRawSign();
|
||||
$_SESSION ["time"] = time();
|
||||
$_SESSION ["time"] = time();
|
||||
}
|
||||
|
||||
static function getRawSign(): string
|
||||
private static function getBrowserSign()
|
||||
{
|
||||
$rawSign = self::SESSION_BROWSER_SIGN_SECRET;
|
||||
$signParts = ['HTTP_USER_AGENT'];
|
||||
// $signParts = array('HTTP_USER_AGENT', 'HTTP_ACCEPT_ENCODING');
|
||||
$signParts = array();
|
||||
|
||||
foreach ($signParts as $signPart) {
|
||||
$rawSign .= '::' . ($_SERVER[$signPart] ?? 'none');
|
||||
$rawSign .= '::' . (isset($_SERVER[$signPart]) ? $_SERVER[$signPart] : 'none');
|
||||
}
|
||||
|
||||
return $rawSign;
|
||||
return md5($rawSign);
|
||||
}
|
||||
|
||||
static function getBrowserSign(): string
|
||||
{
|
||||
return md5(self::getRawSign());
|
||||
}
|
||||
|
||||
function logout(): void {
|
||||
static function logout() {
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,46 +3,26 @@
|
|||
/**
|
||||
* Попытка реализовать фильтр для запросов
|
||||
*/
|
||||
namespace ctiso\Filter;
|
||||
|
||||
use ctiso\Database;
|
||||
use ctiso\HttpRequest;
|
||||
use ctiso\Controller\ActionInterface;
|
||||
|
||||
class Filter implements ActionInterface
|
||||
class Filter_Filter
|
||||
{
|
||||
/** @var ActionInterface */
|
||||
public $processor;
|
||||
|
||||
/**
|
||||
* @param ActionInterface $processor
|
||||
*/
|
||||
public function __construct($processor)
|
||||
public function __construct($processor/*: Controller_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 = '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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Filter;
|
||||
use ctiso\HttpRequest;
|
||||
|
||||
interface FilterInterface {
|
||||
/**
|
||||
* @param HttpRequest $request
|
||||
* @return mixed
|
||||
*/
|
||||
function execute(HttpRequest $request);
|
||||
}
|
||||
|
|
@ -2,96 +2,68 @@
|
|||
|
||||
/**
|
||||
* Фильтр для проверки авторизации
|
||||
*
|
||||
* 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;
|
||||
|
||||
// В класс авторизации передавать обьект для управления пользователем
|
||||
// Вынести в отдельный файл
|
||||
class Login extends Filter
|
||||
class Filter_Login extends Filter_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;
|
||||
/*
|
||||
* Проверка пары логин/пароль
|
||||
*/
|
||||
public function checkLoginPasswordPair($db, $login, $password)
|
||||
{
|
||||
$result = Filter_UserAccess::getUserByLogin($login); // Поиск по логину
|
||||
if ($result) {
|
||||
$userPassword = $result->getString('password');
|
||||
if (Filter_UserAccess::$access == 'site_root' && defined('PARENT_PATH')) {
|
||||
$s = new Settings(PARENT_PATH . '/settings.json');
|
||||
$s->read();
|
||||
$dsn = $s->readKey(array('system', 'dsn'));
|
||||
|
||||
function __construct($processor, User $role, Registry $config) {
|
||||
parent::__construct($processor);
|
||||
$this->role = $role;
|
||||
$this->config = $config;
|
||||
$db = Database::getConnection($dsn);
|
||||
$user = $db->fetchOneArray("SELECT * FROM users WHERE login = :login", ['login' => $login]);
|
||||
$userPassword = $user['password'];
|
||||
} /*else if (time() - $result->getInt('lastupdate') > 60*60*24*60) {
|
||||
// Проверить давность пароля, 60 дней
|
||||
$request->set('error', true);
|
||||
$request->set('lastupdate', true);
|
||||
return false;
|
||||
}*/
|
||||
|
||||
// Извлечение пользователя из родительской CMS, для проверки пароля
|
||||
if (md5($password) == $userPassword) { // password
|
||||
$this->enter($db, $result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка авторизации
|
||||
* @param HttpRequest $request
|
||||
* @return bool Авторизовани пользователь или нет
|
||||
* @return Boolean Авторизован пользователь или нет
|
||||
*/
|
||||
public function isLoggin(HttpRequest $request)
|
||||
{
|
||||
// Авторизация
|
||||
session_start();
|
||||
$db = $this->getConnection();
|
||||
Filter_UserAccess::setUp($db); // Соединение
|
||||
|
||||
switch ($request->getAction()) {
|
||||
// Авторизация по постоянному паролю
|
||||
case 'api_key':
|
||||
case 'login':
|
||||
$login = $request->getString('login', '') ;
|
||||
$password = $request->getString('password');
|
||||
|
||||
$result = $this->role->getUserByLogin($login); // Поиск по логину
|
||||
if ($result) {
|
||||
$userPassword = $this->role->getUserPassword($result);
|
||||
if ($this->role->access == 'site_root' && defined('PARENT_PATH')) {
|
||||
$s = new Settings(PARENT_PATH . '/settings.json');
|
||||
$s->read();
|
||||
$dsn = $s->readKey(['system', 'dsn']);
|
||||
|
||||
$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 дней
|
||||
$request->set('error', true);
|
||||
$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*/) {
|
||||
if (time() - $lastAttempt->get('trie_time') < self::AUTH_LAST_ATTEMPT_TIMER) {
|
||||
$request->set('timeout_error', true);
|
||||
break;
|
||||
} else {
|
||||
$this->role->resetTries($request->getString('login'));
|
||||
}
|
||||
}
|
||||
// Извлечнеие пользователя из родительской CMS, для проверки пароля
|
||||
if (md5($password) == $userPassword) { // password
|
||||
$this->enter($result);
|
||||
return true;
|
||||
} else {
|
||||
// Обновление количества неудачных попыток входа
|
||||
$this->role->updateTries($login);
|
||||
}
|
||||
$login = $request->get('login');
|
||||
$password = $request->get('password');
|
||||
if($this->checkLoginPasswordPair($db,$login,$password)==true){
|
||||
return true;
|
||||
}
|
||||
$request->set('error', true);
|
||||
break;
|
||||
|
|
@ -103,22 +75,22 @@ class Login extends Filter
|
|||
case 'enter':
|
||||
$login = $request->get('login');
|
||||
$password = $request->get('sid');
|
||||
$result = $this->role->getUserByLogin($login); // Поиск по логину
|
||||
$result = Filter_UserAccess::getUserByLogin($login); // Поиск по логину
|
||||
if ($result) {
|
||||
$temp = md5($result->getString('password') . $result->getString('login') . $result->getString('sid'));
|
||||
if ($password == $temp) {
|
||||
$this->enter($result);
|
||||
$this->enter($db, $result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
$hash = Authorization::getBrowserSign();
|
||||
$hash = $this->getBrowserSign();
|
||||
// Если $hash не совпадает $_SESSION['hash'] то удаляем сессию
|
||||
if (isset($_SESSION ['access']) && isset($_SESSION[self::SESSION_BROWSER_SIGN_KEYNAME])) {
|
||||
if ($hash == $_SESSION[self::SESSION_BROWSER_SIGN_KEYNAME]) {
|
||||
$this->user = $user = $this->role->getUserById($_SESSION['access']); // Поиск по идентификатору
|
||||
$this->user = $user = Filter_UserAccess::getUserById($_SESSION['access']); // Поиск по идентификатору
|
||||
if ($user && isset($_SESSION['random']) && ($user->get('sid') == $_SESSION['random'])) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -131,34 +103,41 @@ class Login extends Filter
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вход в систему
|
||||
* @param PDOStatement $result
|
||||
*/
|
||||
private function enter($result): void
|
||||
private function getBrowserSign()
|
||||
{
|
||||
$rawSign = self::SESSION_BROWSER_SIGN_SECRET;
|
||||
//$signParts = array('HTTP_USER_AGENT', 'HTTP_ACCEPT_ENCODING');
|
||||
$signParts = array();
|
||||
|
||||
foreach ($signParts as $signPart) {
|
||||
$rawSign .= '::' . (isset($_SERVER[$signPart]) ? $_SERVER[$signPart] : 'none');
|
||||
}
|
||||
return md5($rawSign);
|
||||
}
|
||||
|
||||
private function enter($db, $result)
|
||||
{
|
||||
$this->user = $result;
|
||||
$random = rand(0, 1024 * 1024);
|
||||
$this->role->setSID((string)$random, $result);
|
||||
$db->executeQuery("UPDATE users SET sid = '$random' WHERE id_user = " . $result->getInt('id_user'));
|
||||
|
||||
$_SESSION["group"] = $result->getInt('access');
|
||||
$_SESSION["access"] = $result->getInt('id_user'); // id_user
|
||||
$_SESSION["random"] = $random; // id_user
|
||||
$_SESSION[self::SESSION_BROWSER_SIGN_KEYNAME] = Authorization::getBrowserSign();
|
||||
$_SESSION[self::SESSION_BROWSER_SIGN_KEYNAME] = $this->getBrowserSign();
|
||||
$_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') {
|
||||
$logged = $this->isLoggin($request);
|
||||
// return json_encode(["logged"=>$logged]);
|
||||
if ($request->get('action') == 'user_access') {
|
||||
if ($logged) {
|
||||
$result = [];
|
||||
$result = array();
|
||||
$result['fullname'] = $this->user->getString('patronymic') . " " . $this->user->getString('firstname');
|
||||
$result['email'] = $this->user->getString('email');
|
||||
$result['site'] = 187;
|
||||
$result['hash'] = sha1(self::SESSION_BROWSER_SIGN_SECRET . $this->user->getString('email'));
|
||||
return json_encode($result);
|
||||
} else {
|
||||
|
|
@ -166,32 +145,34 @@ class Login extends Filter
|
|||
}
|
||||
}
|
||||
|
||||
if ($request->getString('action') == 'relogin') {
|
||||
if($logged && ($request->get('action') == 'api_key')) {
|
||||
$result['key'] = Security::generateAPIKey(strval(time()));
|
||||
return json_encode($result);
|
||||
}
|
||||
|
||||
if ($request->get('action') == 'relogin') {
|
||||
if ($logged) {
|
||||
return json_encode(['result' => 'ok', 'message' => "Авторизация успешна"]);
|
||||
return json_encode(array('result' => 'ok', 'message' => "Авторизация успешна"));
|
||||
} else {
|
||||
return json_encode(['result' => 'fail', 'message' => "Неправильное имя пользователя или пароль"]);
|
||||
return json_encode(array('result' => 'fail', 'message' => "Неправильное имя пользователя или пароль"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$logged) {
|
||||
// Параметры при неправильной авторизации
|
||||
// Действия по умолчанию !! Возможно переход на форму регистрации
|
||||
if ($request->getString('mode') == 'ajax') {
|
||||
if (!$this->requestIsWhite($request)) {
|
||||
return json_encode(['result' => 'fail', 'message' =>"NOT_AUTHORIZED"]);
|
||||
if ($request->get('mode') == 'ajax') {
|
||||
if ((!$this->requestIsWhite($request))&&(!$this->APIKeyCheck($request))) {
|
||||
return json_encode(array('result' => 'fail', 'message' =>"NOT_AUTHORIZED"));
|
||||
}
|
||||
} else {
|
||||
$request->set('module', 'login');
|
||||
$request->set('mode', $this->mode);
|
||||
}
|
||||
} 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')
|
||||
{
|
||||
$arr = array();
|
||||
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,27 +181,69 @@ 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');
|
||||
$moduleDir = explode('_',$module)[0];
|
||||
$file = Path::join(CMS_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;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
* Проверка на попадание реквеста в API_key_allowed и проверка ключа API
|
||||
*/
|
||||
|
||||
public function APIKeyCheck(Collection $request) {
|
||||
$key = $request->get('api_key');
|
||||
if(!is_string($key)) {
|
||||
return false;
|
||||
}
|
||||
$module = $request->get('module');
|
||||
$action = $request->get('action');
|
||||
|
||||
$moduleDir = explode('_',$module)[0];
|
||||
$file = Path::join(CMS_PATH, 'modules', $moduleDir, 'filters', 'api_key.json');
|
||||
if (file_exists($file)) {
|
||||
$whiteList = json_decode(file_get_contents($file), true);
|
||||
|
||||
if (in_array($action, $whiteList)) {
|
||||
if($this->isValidAPIKey($key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* ключ API имеет вид [unixTime]_[hash(unixTime,WWW_PATH)]
|
||||
*/
|
||||
public function isValidAPIKey(String $key) {
|
||||
$parts = explode('_',$key,2);
|
||||
if(count($parts)!=2) {
|
||||
return false;
|
||||
}
|
||||
$timestamp = intval($parts[0]);
|
||||
$timediff = time()-$timestamp;
|
||||
if(($timediff<0)||($timediff>60*60*24)) { // key is valid for 24 hours
|
||||
return false;
|
||||
}
|
||||
if($key != Security::generateAPIKey($timestamp)){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
69
src/Filter/UserAccess.php
Normal file
69
src/Filter/UserAccess.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
// Класс должен быть в библиотеке приложения
|
||||
class Filter_UserAccess
|
||||
{
|
||||
const LIFE_TIME = 1800; // = 30min * 60sec;
|
||||
|
||||
static $fullname;
|
||||
static $name;
|
||||
static $access;
|
||||
static $password;
|
||||
static $id;
|
||||
static $db;
|
||||
|
||||
protected function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function setUp(Database $db)
|
||||
{
|
||||
self::$db = $db;
|
||||
}
|
||||
|
||||
public static function getUserByQuery(Database_Statement $stmt)
|
||||
{
|
||||
global $GROUPS;
|
||||
$result = $stmt->executeQuery();
|
||||
if ($result->next()) {
|
||||
self::$access = $GROUPS[$result->getString('access')];
|
||||
self::$name = $result->getString('login');
|
||||
self::$id = $result->getInt('id_user');
|
||||
self::$password = $result->getString('password');
|
||||
self::$fullname = implode(' ', array(
|
||||
$result->getString('surname'),
|
||||
$result->getString('firstname'),
|
||||
$result->getString('patronymic')));
|
||||
return $result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getUserByLogin($login)
|
||||
{
|
||||
$stmt = self::$db->prepareStatement("SELECT * FROM users WHERE login = ?");
|
||||
$stmt->setString(1, $login);
|
||||
$result = self::getUserByQuery($stmt);
|
||||
if ($result) {
|
||||
$time = time();
|
||||
$id = self::$id;
|
||||
self::$db->executeQuery("UPDATE users SET lasttime = $time WHERE id_user = $id"); // Время входа
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getUserById($id)
|
||||
{
|
||||
$stmt = self::$db->prepareStatement("SELECT * FROM users WHERE id_user = ?");
|
||||
$stmt->setInt(1, $_SESSION ['access']);
|
||||
$result = self::getUserByQuery($stmt);
|
||||
if ($result) {
|
||||
$lasttime = $result->getInt('lasttime');
|
||||
$time = time();
|
||||
if ($time - $lasttime > self::LIFE_TIME) return null; // Вышло время сессии
|
||||
$id = self::$id;
|
||||
// self::$db->executeQuery("UPDATE users SET lasttime = $time WHERE id_user = $id"); // Время последнего обращения входа
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Input;
|
||||
|
||||
class BrowserInput extends Input {
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Field;
|
||||
|
||||
class CheckBox extends Field
|
||||
{
|
||||
/** @var bool */
|
||||
public $checked = false;
|
||||
function setValue($value): void
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->checked = $value;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,6 @@
|
|||
/**
|
||||
* Поле с цветом
|
||||
*/
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Field;
|
||||
|
||||
class Color extends Field
|
||||
class Form_Color extends Form_Field
|
||||
{
|
||||
}
|
||||
|
|
@ -2,8 +2,5 @@
|
|||
/**
|
||||
* Поле с датой
|
||||
*/
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Field;
|
||||
|
||||
class Date extends Field {
|
||||
class Form_Date extends Form_Field {
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Input;
|
||||
|
||||
class DateTime extends Input {
|
||||
}
|
||||
|
|
@ -2,53 +2,27 @@
|
|||
/**
|
||||
* Элемент формы
|
||||
*/
|
||||
namespace ctiso\Form;
|
||||
|
||||
use ctiso\Form\OptionsFactory;
|
||||
|
||||
class Field
|
||||
class Form_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 = [];
|
||||
|
||||
public $alias = array();
|
||||
|
||||
/**
|
||||
* @param array $input
|
||||
* @param OptionsFactory|null $factory
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
public function __construct ($input = [], $factory = null)
|
||||
public function __construct ($input = array(), $factory = null)
|
||||
{
|
||||
|
||||
$this->default = null;
|
||||
if (isset($input['validate'])) {
|
||||
$this->require = strpos($input['validate'], 'require') !== false;
|
||||
|
|
@ -57,22 +31,19 @@ class Field
|
|||
$this->fieldset = $input['fieldset'];
|
||||
}
|
||||
// Инициализация свойст обьетка
|
||||
foreach (['label', 'name', 'type', 'description', 'maxlength'] as $name) {
|
||||
foreach (array('label', 'name', 'type', 'description') as $name) {
|
||||
if (isset($input[$name])) {
|
||||
$this->$name = $input[$name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
function setValue($value): void
|
||||
function setValue($value/*: any*/)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
function getId(): string
|
||||
function getId()
|
||||
{
|
||||
return $this->name . '_label';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,100 +4,115 @@
|
|||
* При рендеринге каждому классу соответствует шаблон (см. themes/maxim/templates/macros.html)
|
||||
*/
|
||||
|
||||
namespace ctiso\Form;
|
||||
class TCheckbox extends Form_Field
|
||||
{
|
||||
public $checked = false;
|
||||
function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->checked = $value;
|
||||
}
|
||||
}
|
||||
|
||||
use ctiso\Form\Field;
|
||||
use ctiso\Form\Select;
|
||||
use ctiso\Form\Input;
|
||||
use ctiso\Validator\Validator;
|
||||
use ctiso\HttpRequest;
|
||||
class TQuestionType extends Form_Select
|
||||
{
|
||||
function setValue($value)
|
||||
{
|
||||
// Установить selected у options
|
||||
$this->value = $value;
|
||||
foreach ($this->options as &$option) {
|
||||
$option['selected'] = ($option['value'] == $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TDateTime extends Form_Input {
|
||||
}
|
||||
|
||||
/**
|
||||
* Поле для ввода пароля
|
||||
*/
|
||||
class TSecret extends Form_Field {
|
||||
}
|
||||
|
||||
class TUpload extends Form_Field {
|
||||
}
|
||||
|
||||
class THidden extends Form_Input {
|
||||
public $hidden = true;
|
||||
}
|
||||
|
||||
class TComponentBrowserInput extends Form_Input {
|
||||
}
|
||||
|
||||
/**
|
||||
* Форма для ввода
|
||||
*/
|
||||
class Form {
|
||||
/** @var array<Field> */
|
||||
public $field = []; //Поля формы
|
||||
/** @var array */
|
||||
public $fieldsets = []; //Группы полей (fieldset). Некоторые поля могут не принадлежать никаким группам
|
||||
class Form_Form extends View_View {
|
||||
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();
|
||||
public $constructor = array();
|
||||
|
||||
/**
|
||||
* Строим форму по ее структуре. Каждому типу соответствует определенный класс.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->constructor = [
|
||||
'input' => Input::class,
|
||||
// input с проверкой на заполненность
|
||||
'inputreq' => Input::class,
|
||||
'date' => Date::class,
|
||||
'datereq' => Date::class,
|
||||
'datetime' => DateTime::class,
|
||||
$this->constructor = array(
|
||||
'input' => 'Form_Input',
|
||||
'inputreq' => 'Form_Input', // input с проверкой на заполненность
|
||||
|
||||
'color' => Color::class,
|
||||
'textarea' => TextArea::class,
|
||||
'text' => TextArea::class,
|
||||
'multiselect' => SelectMany::class,
|
||||
'select1' => SelectOne::class,
|
||||
'select' => SelectOne::class,
|
||||
'date' => 'Form_Date',
|
||||
'datereq' => 'Form_Date',
|
||||
'datetime' => 'TDateTime',
|
||||
|
||||
'questiontype'=> QuestionType::class,
|
||||
'secret' => Secret::class,
|
||||
'upload' => Upload::class,
|
||||
'image' => Upload::class,
|
||||
'checkbox' => CheckBox::class,
|
||||
'checkmany' => SelectMany::class,
|
||||
'hidden' => Hidden::class,
|
||||
'radio' => SelectOne::class,
|
||||
'filebrowser' => BrowserInput::class,
|
||||
'documents' => BrowserInput::class,
|
||||
'chooser' => Input::class,
|
||||
'select_chooser' => SelectOne::class,
|
||||
'html_text' => HtmlText::class
|
||||
];
|
||||
'color' => 'Form_Color',
|
||||
'textarea' => 'Form_TextArea',
|
||||
'text' => 'Form_TextArea',
|
||||
'multiselect' => 'Form_SelectMany',
|
||||
// 'selectmany' => 'TSelectMany',
|
||||
'select1' => 'Form_SelectOne',
|
||||
'select' => 'Form_SelectOne',
|
||||
'questiontype'=> 'TQuestionType',
|
||||
'secret' => 'TSecret',
|
||||
'upload' => 'TUpload',
|
||||
'image' => 'TUpload',
|
||||
'checkbox' => 'TCheckbox',
|
||||
'checkmany' => 'Form_SelectMany',
|
||||
'hidden' => 'THidden',
|
||||
'radio' => 'Form_SelectOne',
|
||||
'filebrowser' => 'TComponentBrowserInput',
|
||||
'documents' => 'TComponentBrowserInput',
|
||||
'chooser' => 'Form_Input',
|
||||
'select_chooser' => 'Form_SelectOne'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function getId(): string
|
||||
function getId()
|
||||
{
|
||||
return '_form_edit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление конструкторя для поля формы
|
||||
* @param string $name Краткое название поля
|
||||
* @param class-string<Field> $class
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public function addField(array $init, $factory = null): Field
|
||||
public function addField(array $init, $factory = null)
|
||||
{
|
||||
assert(isset($init['type']));
|
||||
assert(isset($init['name']));
|
||||
|
|
@ -111,15 +126,16 @@ class Form {
|
|||
if(isset($init['hint'])) {
|
||||
$el->hint = $init['hint'];
|
||||
}
|
||||
|
||||
$this->field[$init['name']] = $el;
|
||||
|
||||
$this->field [$init['name']] = $el;
|
||||
return $el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление fieldset на форму
|
||||
*/
|
||||
public function addFieldSet(array $fieldset): void
|
||||
|
||||
public function addFieldSet(array $fieldset)
|
||||
{
|
||||
$this->fieldsets[$fieldset['name']] = $fieldset;
|
||||
}
|
||||
|
|
@ -127,7 +143,8 @@ class Form {
|
|||
/**
|
||||
* Добавление массива fieldset на форму
|
||||
*/
|
||||
public function addFieldSetList(array $list): void
|
||||
|
||||
public function addFieldSetList(array $list)
|
||||
{
|
||||
foreach ($list as $fieldset) {
|
||||
$this->addFieldSet($fieldset);
|
||||
|
|
@ -136,10 +153,9 @@ class Form {
|
|||
|
||||
/**
|
||||
* Добавляет список полей для формы
|
||||
* @param array $list
|
||||
* @param OptionsFactory|null $factory
|
||||
* @param array $list
|
||||
*/
|
||||
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 +165,7 @@ class Form {
|
|||
/**
|
||||
* Устанавливает ошибки после проверки
|
||||
*/
|
||||
function setError(Validator $validator): void
|
||||
function setError(Validator_Validator $validator)
|
||||
{
|
||||
foreach ($validator->getErrorMsg() as $name => $error)
|
||||
{
|
||||
|
|
@ -158,12 +174,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;
|
||||
|
|
@ -171,41 +182,33 @@ class Form {
|
|||
|
||||
/**
|
||||
* Устанавливает значения из масива
|
||||
*/
|
||||
function setValues(HttpRequest $request): void {
|
||||
foreach ($this->field as $key => $_) {
|
||||
*/
|
||||
function setValues(HttpRequest $request) {
|
||||
foreach ($this->field as $key => $el) {
|
||||
$value = $request->getRawData($this->method, $key);
|
||||
$this->field[$key]->setValue($value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Заполняет форму данными из обьекта
|
||||
* Заполняет форму данными из обьекта
|
||||
* @param object $data
|
||||
* @param array $schema Связь между элементами формы и свойствами обьекта
|
||||
* @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(array('Primitive', '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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Input;
|
||||
|
||||
class Hidden extends Input {
|
||||
/** @var bool */
|
||||
public $hidden = true;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Field;
|
||||
|
||||
// вставка простого html
|
||||
class HtmlText extends Field {
|
||||
}
|
||||
|
|
@ -3,8 +3,5 @@
|
|||
/**
|
||||
* Поле ввода Input
|
||||
*/
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Field;
|
||||
|
||||
class Input extends Field {
|
||||
class Form_Input extends Form_Field {
|
||||
}
|
||||
87
src/Form/OptionFactory.php
Normal file
87
src/Form/OptionFactory.php
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
class Form_OptionFactory {
|
||||
public $db;
|
||||
public $registry;
|
||||
|
||||
function __construct($db, $registry = null) {
|
||||
$this->db = $db;
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
function create(Form_Select $field, $input) {
|
||||
if (isset($input['options.resid'])) {
|
||||
$type = $input['options.resid'];
|
||||
|
||||
$res = new Model_Resources($this->db);
|
||||
$field->options = $this->optionsArray('id_section', 'title', $res->getSubsections('', $type));
|
||||
|
||||
} else if (isset($input['options.res'])) {
|
||||
$type = $input['options.res'];
|
||||
|
||||
$res = new Model_Resources($this->db);
|
||||
$field->options = $this->optionsArray('path', 'title', $res->getSubsections('', $type));
|
||||
|
||||
} else if (isset($input['options.all_res'])) {
|
||||
$type = $input['options.all_res'];
|
||||
|
||||
$res = new Model_Resources($this->db);
|
||||
$field->options = $this->optionsArray('id_resource', 'subtitle', $res->getAllResource($type));
|
||||
|
||||
} else if (isset($input['options.db'])) {
|
||||
list($table, $keyvalue) = explode(":", $input['options.db']);
|
||||
list($key, $value) = explode(",", $keyvalue);
|
||||
try {
|
||||
$query_result = $this->db->executeQuery("SELECT * FROM $table");
|
||||
$field->options = $this->optionsDB($key, $value, $query_result);
|
||||
} catch(Exception $ex) {
|
||||
$field->options = [];
|
||||
}
|
||||
} elseif (isset($input['options.pair'])) {
|
||||
$field->options = $this->optionsPair($input['options.pair']);
|
||||
} elseif (isset($input['options.model'])) {
|
||||
$factory = new Model_Factory($this->db, $this->registry);
|
||||
$model = $factory->getModel($input['options.model']);
|
||||
$field->options = $model->getAllAsOptions();
|
||||
} else {
|
||||
$field->options = $input['options'];
|
||||
}
|
||||
if (isset($input['default'])) {
|
||||
array_unshift($field->options, ['value' => 0, 'name' => $input['default']]);
|
||||
}
|
||||
|
||||
// Ставим корневой каталог в начало списка (скорее всего он будет в конце массива)
|
||||
if ($field->options)
|
||||
{
|
||||
$root_elem = array_pop($field->options);
|
||||
if ($root_elem['value'] == '/')
|
||||
array_unshift($field->options, $root_elem);
|
||||
else
|
||||
array_push($field->options, $root_elem);
|
||||
}
|
||||
}
|
||||
|
||||
public function optionsDB($key, $val, $res) {
|
||||
$result = array();
|
||||
while($res->next()) {
|
||||
$result[] = array('value' => $res->getInt($key), 'name' => $res->getString($val));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function optionsArray($key, $val, $res) {
|
||||
$result = array();
|
||||
foreach($res as $item) {
|
||||
$result[] = array('value' => $item->{$key}, 'name' => $item->{$val});
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function optionsPair($list, $selected = false) {
|
||||
$result = array();
|
||||
foreach ($list as $key => $value) {
|
||||
$result [] = array('value' => $key, 'name' => $value, 'selected' => $key == $selected);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
|
||||
interface OptionsFactory {
|
||||
function create(Select $field, array $options): void;
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Select;
|
||||
|
||||
class QuestionType extends Select
|
||||
{
|
||||
function setValue($value): void
|
||||
{
|
||||
// Установить selected у options
|
||||
$this->value = $value;
|
||||
foreach ($this->options as &$option) {
|
||||
$option['selected'] = ($option['value'] == $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Field;
|
||||
|
||||
/**
|
||||
* Поле для ввода пароля
|
||||
*/
|
||||
class Secret extends Field {
|
||||
}
|
||||
|
|
@ -1,22 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Field;
|
||||
|
||||
/**
|
||||
* @phpstan-type Option = array{value: string, name: string, selected?: bool, class?: string|false}
|
||||
*/
|
||||
class Select extends Field
|
||||
class Form_Select extends Form_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);
|
||||
parent::__construct($input, $factory);
|
||||
|
||||
if ($factory != null) {
|
||||
$factory->create($this, $input);
|
||||
|
|
@ -32,15 +21,10 @@ class Select extends Field
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $list
|
||||
* @param bool $selected
|
||||
* @return Option[]
|
||||
*/
|
||||
public function optionsPair($list, $selected = false) {
|
||||
$result = [];
|
||||
$result = array();
|
||||
foreach ($list as $key => $value) {
|
||||
$result [] = ['value' => $key, 'name' => $value, 'selected' => $key == $selected];
|
||||
$result [] = array('value' => $key, 'name' => $value, 'selected' => $key == $selected);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Select;
|
||||
|
||||
class SelectMany extends Select
|
||||
class Form_SelectMany extends Form_Select
|
||||
{
|
||||
function setValue(mixed $value): void
|
||||
function setValue($value)
|
||||
{
|
||||
// Установить selected у options
|
||||
if (!is_array($value)) { $value = [$value]; }
|
||||
if (!is_array($value)) { $value = array($value); }
|
||||
$this->value = $value;
|
||||
foreach ($this->options as &$option) {
|
||||
$option['selected'] = (in_array($option['value'], $value));
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@
|
|||
/**
|
||||
* Выбор из одного элемента
|
||||
*/
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Select;
|
||||
|
||||
class SelectOne extends Select
|
||||
class Form_SelectOne extends Form_Select
|
||||
{
|
||||
function setValue($value): void
|
||||
function setValue($value)
|
||||
{
|
||||
// Установить selected у options
|
||||
$this->value = $value;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Текстовое поле
|
||||
* Текстовое поле
|
||||
*/
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Field;
|
||||
|
||||
class TextArea extends Field {
|
||||
function setValue($value): void
|
||||
class Form_TextArea extends Form_Field {
|
||||
function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Form;
|
||||
use ctiso\Form\Field;
|
||||
|
||||
class Upload extends Field {
|
||||
}
|
||||
|
|
@ -1,33 +1,21 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
/**
|
||||
* http://www.alternateinterior.com/2006/09/a-viewstate-for-php.html
|
||||
* Управление состоянием между страницами
|
||||
*/
|
||||
namespace ctiso\Form;
|
||||
|
||||
class ViewState // extends Collection
|
||||
class Form_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 +26,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;
|
||||
|
|
|
|||
|
|
@ -1,84 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso;
|
||||
/**
|
||||
* Функциональное программирование в PHP
|
||||
* package functional
|
||||
*/
|
||||
|
||||
/**
|
||||
* Эмуляция каррированой функции
|
||||
*/
|
||||
class right {
|
||||
/** @var array<mixed> */
|
||||
protected $params;
|
||||
/** @var callable */
|
||||
class __right {
|
||||
protected $params;
|
||||
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 */
|
||||
class __left {
|
||||
protected $params;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
define('__', '_ARGUMENT_PLACE_');
|
||||
class partial {
|
||||
/** @var array<mixed> */
|
||||
protected $params;
|
||||
/** @var callable */
|
||||
class __partial {
|
||||
protected $params;
|
||||
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) {
|
||||
$result = [];
|
||||
$count = count($this->params);
|
||||
for($i = 0, $j = 0; $i < $count; $i++) {
|
||||
function apply() {
|
||||
$params = func_get_args();
|
||||
$result = array();
|
||||
for($i = 0, $j = 0; $i < count($this->params); $i++) {
|
||||
if ($this->params[$i] == __) {
|
||||
$result [] = $params[$j];
|
||||
$j++;
|
||||
|
|
@ -88,31 +63,21 @@ class partial {
|
|||
}
|
||||
return call_user_func_array ($this->fn, $result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Композиция функций
|
||||
*/
|
||||
class compose {
|
||||
/** @var array<callable> */
|
||||
class __compose {
|
||||
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++) {
|
||||
for ($i = 1; $i < count($this->fns); $i++) {
|
||||
$result = call_user_func($this->fns[$i], $result);
|
||||
}
|
||||
return $result;
|
||||
|
|
@ -121,57 +86,54 @@ class compose {
|
|||
|
||||
class Functions {
|
||||
|
||||
/**
|
||||
* Частичное применение функции
|
||||
* @param mixed ...$args
|
||||
* @return mixed
|
||||
*/
|
||||
static function partial(...$args) {
|
||||
$closure = new partial($args);
|
||||
return [$closure, 'apply'];
|
||||
static function partial() {
|
||||
$closure = new __partial(func_get_args());
|
||||
return array($closure, 'apply');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Композиция функций
|
||||
* @param mixed ...$args
|
||||
* @return mixed
|
||||
* @param mixed $a
|
||||
* @param mixed $b
|
||||
*
|
||||
* @return array[int]mixed
|
||||
*/
|
||||
static function compose(...$args) {
|
||||
$closure = new compose($args);
|
||||
return [$closure, 'apply'];
|
||||
static function compose() {
|
||||
$closure = new __compose(func_get_args());
|
||||
return array($closure, 'apply');
|
||||
}
|
||||
|
||||
/**
|
||||
* Карирование справа
|
||||
* @param mixed ...$args
|
||||
* @return mixed
|
||||
*
|
||||
* @return array[int]mixed
|
||||
*/
|
||||
static function rcurry(...$args) {
|
||||
$closure = new right($args);
|
||||
return [$closure, 'apply'];
|
||||
static function rcurry($_rest) {
|
||||
$closure = new __right(func_get_args ());
|
||||
return array($closure, 'apply');
|
||||
}
|
||||
|
||||
/**
|
||||
* Карирование слева
|
||||
* @param mixed ...$args
|
||||
* @return mixed
|
||||
*
|
||||
* @return array[int]mixed
|
||||
*/
|
||||
static function lcurry(...$args) {
|
||||
$closure = new left($args);
|
||||
return [$closure, 'apply'];
|
||||
static function lcurry($_rest) {
|
||||
$closure = new __left(func_get_args ());
|
||||
return array($closure, 'apply');
|
||||
}
|
||||
|
||||
/**
|
||||
* Разделение массива на два по условию
|
||||
* @param mixed $pred Условие по которому разделяется массив
|
||||
* @param array $lst
|
||||
* @param array $lst
|
||||
*
|
||||
* @return mixed
|
||||
* @return array[int]mixed
|
||||
*/
|
||||
static function partition($pred, $lst) {
|
||||
$left = [];
|
||||
$right = [];
|
||||
$left = array ();
|
||||
$right = array ();
|
||||
foreach ($lst as $n) {
|
||||
if (call_user_func($pred, $n) !== false) {
|
||||
$left [] = $n;
|
||||
|
|
@ -179,37 +141,29 @@ class Functions {
|
|||
$right [] = $n;
|
||||
}
|
||||
}
|
||||
return [$left, $right];
|
||||
return array ($left, $right);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param array<string, mixed> $value
|
||||
* @param string $name
|
||||
* @param array $value
|
||||
* @param string $name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
static function __key($value, $name) {
|
||||
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
|
||||
* @return int
|
||||
*/
|
||||
static function __cmp($a, $b, $key) {
|
||||
if ($a[$key] == $b[$key]) {
|
||||
|
|
@ -217,133 +171,108 @@ 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;
|
||||
}
|
||||
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(array($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) {
|
||||
$result = [];
|
||||
|
||||
static function key_values($key, /*array|ArrayIterator*/ $array) {
|
||||
$result = array();
|
||||
|
||||
foreach($array as $item) {
|
||||
$result[] = $item[$key];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param array<object>|\ArrayIterator<int, object> $array
|
||||
* @return array<mixed>
|
||||
*/
|
||||
static function key_values_object($key, $array) {
|
||||
$result = [];
|
||||
|
||||
|
||||
static function key_values_object($key, /*array|ArrayIterator*/ $array) {
|
||||
$result = array();
|
||||
|
||||
foreach($array as $item) {
|
||||
$result[] = $item->{$key};
|
||||
}
|
||||
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 = [];
|
||||
$result = array();
|
||||
foreach ($array as $item) {
|
||||
$result[$item[$key]] = $item[$value];
|
||||
}
|
||||
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 = [];
|
||||
$result = array();
|
||||
foreach ($array as $item) {
|
||||
$result[$item[$key]] = $item;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Логическа операция && ко всем элементам массива
|
||||
* @param array<mixed> $array Массив
|
||||
* @param callable $callback Функция
|
||||
* @return bool
|
||||
*/
|
||||
static function every(array $array, $callback) {
|
||||
|
|
@ -354,14 +283,17 @@ class Functions {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Логическа операция || ко всем элементам массива
|
||||
* @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;
|
||||
|
|
@ -369,107 +301,80 @@ class Functions {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Разбивает массив на массивы определенной длины
|
||||
* @template T
|
||||
* @param int $length Длина массива
|
||||
* @param T[] $array Массив
|
||||
* @return T[][]
|
||||
*/
|
||||
static function span(int $length, array $array) {
|
||||
$result = [];
|
||||
$count = count($array);
|
||||
for($i = 0; $i < $count; $i += $length) {
|
||||
|
||||
static function span($length, array $array) {
|
||||
assert(is_int($length));
|
||||
|
||||
$result = array();
|
||||
for($i = 0; $i < count($array); $i += $length) {
|
||||
$result [] = array_slice($array, $i, $length);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает значение массива
|
||||
* @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 function $cb сравнение с элементом массива
|
||||
* @param array $hs массив в котором ищется значение
|
||||
* @param bool $strict
|
||||
*
|
||||
* @return int|string|null ключ найденого элемента в массиве
|
||||
* @return int|string ключ найденого элемента в массиве
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach($hs as $key => $value) if (call_user_func_array($cb, array($value, $key, $strict))) return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выбирает все сроки из таблицы с уникальными значениями ключа
|
||||
* @param $name Имя ключа
|
||||
* @param $table Двухмерный массив
|
||||
* @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 Массив с уникальными значениями ключа
|
||||
=> array (1, 2)
|
||||
* @end example
|
||||
*/
|
||||
static function key_unique_values ($name, $table) {
|
||||
// Ищем уникальные значения для заданного ключа
|
||||
$keys = [];
|
||||
// Ищем уникальные значения для заданного ключа
|
||||
$keys = array ();
|
||||
foreach ($table as $row) {
|
||||
if (!in_array ($row[$name], $keys)) {
|
||||
if (!in_array ($row[$name], $keys))
|
||||
$keys[] = $row[$name];
|
||||
}
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Сортировка двумерного массива по заданному ключу
|
||||
* @param array $array Массив
|
||||
* @param string $key Имя ключа по значению которого будет идти сравнение
|
||||
* @param callable $fn Функция сравнения
|
||||
* @return array Отсортированный массив
|
||||
* @param $array Массив
|
||||
* @param $key Имя ключа по значению которого будет идти сравнение
|
||||
* @return Отсортированный массив
|
||||
*/
|
||||
static function sortOn($array, $key, $fn = '\\ctiso\\Functions::__cmp') {
|
||||
static function sortOn($array, $key, $fn = 'Functions::__cmp') {
|
||||
usort ($array, Functions::rcurry($fn, $key));
|
||||
//usort ($array, create_function ('$x,$y', 'return __cmp ($x, $y, "'.$key.'");'));
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Преобразует ключи элементов для многомерного массива
|
||||
* @param string $key_name Имя ключа
|
||||
* @param array $array Многомерный массив
|
||||
* Преобразует ключи элементов для многомерного массива
|
||||
* @return mixed
|
||||
*/
|
||||
static function hash_key ($key_name, $array) {
|
||||
$result = [];
|
||||
static function hash_key ($key_name,$array/*: array*/) {
|
||||
$result = array();
|
||||
|
||||
foreach($array as $value) {
|
||||
$result[$value[$key_name]] = $value;
|
||||
$result[$value[$key_name]] = $value;
|
||||
}
|
||||
return $result;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,17 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Неверный запрос
|
||||
*/
|
||||
namespace ctiso;
|
||||
|
||||
|
||||
use ctiso\Collection;
|
||||
use ctiso\Session;
|
||||
|
||||
/**
|
||||
* @template T=mixed
|
||||
*/
|
||||
class HttpRequest extends Collection
|
||||
* Неверный запрос
|
||||
*/
|
||||
class WrongRequestException extends Exception
|
||||
{
|
||||
/** @var Session */
|
||||
public $_session;
|
||||
}
|
||||
|
||||
// HTTPRequest = ArrayAccess
|
||||
class HttpRequest extends Collection implements ArrayAccess
|
||||
{
|
||||
|
||||
public $_session;
|
||||
/**
|
||||
* Constructor
|
||||
* Stores "request data" in GPC order.
|
||||
|
|
@ -24,9 +19,9 @@ class HttpRequest extends Collection
|
|||
public function __construct()
|
||||
{
|
||||
$list = [
|
||||
'data' => $_REQUEST,
|
||||
'get' => $_GET,
|
||||
'post' => $_POST,
|
||||
'data' => $_REQUEST,
|
||||
'get' => $_GET,
|
||||
'post' => $_POST,
|
||||
'cookie' => $_COOKIE
|
||||
];
|
||||
|
||||
|
|
@ -42,77 +37,37 @@ 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
|
||||
*/
|
||||
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;
|
||||
$this->_session = $value;
|
||||
}
|
||||
return $this->_session;
|
||||
}
|
||||
|
||||
function set(string $key, mixed $value): void
|
||||
function set($key, $value/*: any*/)
|
||||
{
|
||||
parent::get('data')->set($key, $value);
|
||||
return 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 +77,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 +86,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 +94,37 @@ 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 {
|
||||
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
|
||||
|
||||
public function offsetSet($key, $value) {
|
||||
}
|
||||
|
||||
public function offsetExists($key) {
|
||||
}
|
||||
|
||||
public function offsetUnset($key) {
|
||||
}
|
||||
|
||||
public function offsetGet($key) {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,8 @@
|
|||
/**
|
||||
* Самый простой макет
|
||||
*/
|
||||
namespace ctiso\Layout;
|
||||
use ctiso\Filter\Filter,
|
||||
ctiso\HttpRequest;
|
||||
|
||||
class Blank extends Filter
|
||||
class Layout_Empty extends Filter_Filter
|
||||
{
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
function execute(HttpRequest $request)
|
||||
{
|
||||
$text = $this->processor->execute($request);
|
||||
|
|
@ -4,44 +4,32 @@
|
|||
* Выбор макета страницы.
|
||||
* Выбор оформления страницы осуществляется если было совпадение с каким либо условием
|
||||
*/
|
||||
namespace ctiso\Layout;
|
||||
|
||||
use ctiso\Filter\Filter;
|
||||
use ctiso\HttpRequest;
|
||||
|
||||
class Manager extends Filter
|
||||
class Layout_Manager extends Filter_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_Filter $layout)
|
||||
{
|
||||
$this->addCondition(fn(HttpRequest $request) => $this->checkGet($request, $get), $layout);
|
||||
$this->addCondition(Functions::rcurry(array($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_Filter $layout)
|
||||
{
|
||||
$this->addCondition(fn(HttpRequest $request) => $this->checkXHR($request, $get), $layout);
|
||||
$this->addCondition(Functions::rcurry(array($this, 'checkXHR'), $get), $layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HttpRequest $request
|
||||
* @param array|true $get
|
||||
* @return bool
|
||||
*/
|
||||
public function checkGet($request, $get)
|
||||
{
|
||||
if (is_array($get)) {
|
||||
|
|
@ -54,44 +42,38 @@ 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, $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
|
||||
{
|
||||
$this->condition [] = [$get, $layout];
|
||||
public function addCondition($get, Filter_Filter $layout)
|
||||
{
|
||||
$this->condition [] = array($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();
|
||||
echo $view->render();
|
||||
} else {
|
||||
return $view;
|
||||
echo $view;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
146
src/Mail.php
146
src/Mail.php
|
|
@ -4,75 +4,55 @@
|
|||
* Класс для работы с почтой
|
||||
* 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())); // Идентефикатор разделителя
|
||||
$this->uniqid = strtoupper(uniqid(time())); // Идентефикатор разделителя
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка отправителя
|
||||
*/
|
||||
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 +60,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) ? array($data, $name) : array($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 [] = array($data, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $var
|
||||
* @param string $val
|
||||
*/
|
||||
function quote($var, $val): string
|
||||
function quote($var, $val)
|
||||
{
|
||||
return ";" . PHP_EOL . "\t" . $var . "=\"" . $val . "\"";
|
||||
}
|
||||
|
|
@ -151,48 +120,44 @@ 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 = array())
|
||||
{
|
||||
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(array($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->mimeTag("Content-Type", $this->type, ['charset' => $this->encoding]);
|
||||
$message = "--".$this->uniqid . PHP_EOL;
|
||||
$message .= $this->mimeTag("Content-Type", $this->type, array ('charset' => $this->encoding));
|
||||
$message .= $this->mimeTag("Content-Transfer-Encoding", "8bit");
|
||||
$message .= PHP_EOL . $this->content . PHP_EOL . PHP_EOL;
|
||||
|
||||
/*
|
||||
* Вложения
|
||||
* Вложения
|
||||
* http://tools.ietf.org/html/rfc2046#section-5.1.3
|
||||
*/
|
||||
foreach ($this->attachment as $value) {
|
||||
list($data, $name) = $value;
|
||||
$message .= "--" . $this->uniqid . PHP_EOL;
|
||||
$message .= $this->mimeTag("Content-Type", "application/octet-stream", ['name' => basename($name)]);
|
||||
$message .= $this->mimeTag("Content-Type", "application/octet-stream", array ('name' => basename($name)));
|
||||
$message .= $this->mimeTag("Content-Transfer-Encoding", "base64");
|
||||
$message .= $this->mimeTag("Content-Disposition", "attachment", ['filename' => basename($name)]);
|
||||
$message .= $this->mimeTag("Content-Disposition", "attachment", array ('filename' => basename($name)));
|
||||
$message .= PHP_EOL . chunk_split(base64_encode($data)) . PHP_EOL;
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +167,7 @@ class Mail
|
|||
/**
|
||||
* Заголовок сообщения
|
||||
*/
|
||||
function getHeader(): string
|
||||
function getHeader()
|
||||
{
|
||||
$head = $this->mimeTag("MIME-Version", "1.0");
|
||||
$head .= $this->mimeTag("From", $this->_from);
|
||||
|
|
@ -211,7 +176,7 @@ class Mail
|
|||
if (is_string($this->_notify)) {
|
||||
$head .= $this->mimeTag("Disposition-Notification-To", "\"" . $this->_notify . "\" <" . $this->_from . ">");
|
||||
}
|
||||
$head .= $this->mimeTag("Content-Type", "multipart/mixed", ["boundary" => $this->uniqid]);
|
||||
$head .= $this->mimeTag("Content-Type", "multipart/mixed", array ("boundary" => $this->uniqid));
|
||||
if ($this->copy) {
|
||||
$head .= $this->mimeTag("BCC", $this->copy);
|
||||
}
|
||||
|
|
@ -219,7 +184,7 @@ class Mail
|
|||
return $head;
|
||||
}
|
||||
|
||||
function getSubject(): string
|
||||
function getSubject()
|
||||
{
|
||||
return $this->encodedWord($this->_subject);
|
||||
}
|
||||
|
|
@ -227,29 +192,28 @@ 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 = [];
|
||||
$this->attachment = array();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
|
||||
class MailAlt
|
||||
{
|
||||
/** @var PHPMailer */
|
||||
public $mailer;
|
||||
/** @var string */
|
||||
public $_notify;
|
||||
/** @var string */
|
||||
public $encoding;
|
||||
|
||||
function __construct() {
|
||||
|
|
@ -20,65 +14,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 +77,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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Model;
|
||||
|
||||
/**
|
||||
* @property \ctiso\Database $db
|
||||
*/
|
||||
abstract class BaseMapper {
|
||||
function getAllAsOptions(): array {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Model;
|
||||
|
||||
use ctiso\Registry;
|
||||
use ctiso\Database;
|
||||
use ctiso\Role\User;
|
||||
|
||||
class Factory
|
||||
class Model_Factory
|
||||
{
|
||||
/** @var Database */
|
||||
static $shortcut = "model";
|
||||
public $db;
|
||||
/** @var ?Registry */
|
||||
public $config;
|
||||
/** @var ?User */
|
||||
public $user;
|
||||
public $_registry;
|
||||
public $_shortcut;
|
||||
|
||||
public function __construct(Database $db, ?Registry $config = null, ?User $user = null)
|
||||
public function __construct ($db/*: Database*/, Settings $_registry = null)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->config = $config;
|
||||
$this->user = $user;
|
||||
$this->_registry = $_registry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Создает модель
|
||||
* @template T
|
||||
* @param class-string<T> $modelName
|
||||
* @return T
|
||||
* @param string $name
|
||||
* @return model
|
||||
*/
|
||||
public function getModel($modelName)
|
||||
public function getModel ($name)
|
||||
{
|
||||
$modelName = "Mapper_" . $name;
|
||||
$model = new $modelName();
|
||||
$model->db = $this->db;
|
||||
$model->factory = $this;
|
||||
$model->config = $this->config;
|
||||
$model->user = $this->user;
|
||||
$model->_registry = $this->_registry;
|
||||
$model->setUp();
|
||||
//
|
||||
return $model;
|
||||
|
|
|
|||
|
|
@ -1,30 +1,24 @@
|
|||
<?php
|
||||
|
||||
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 = array();
|
||||
for ($i = 0; $i < count($array); $i++) {
|
||||
$result [] = call_user_func($prefix, $i + 1) . '. ' . $array[$i];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
291
src/Path.php
291
src/Path.php
|
|
@ -6,22 +6,19 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace ctiso;
|
||||
|
||||
class Path
|
||||
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 = '')
|
||||
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'];
|
||||
|
|
@ -36,18 +33,18 @@ class Path
|
|||
|
||||
$this->path = self::optimize($list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 +52,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);
|
||||
}
|
||||
|
||||
|
|
@ -68,19 +65,16 @@ class Path
|
|||
* Возвращает расширение файла
|
||||
*
|
||||
* @param string $fileName Полное имя файла
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static function getExtension($fileName)
|
||||
{
|
||||
assert(is_string($fileName) || is_null($fileName));
|
||||
|
||||
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 +86,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 +107,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,47 +123,47 @@ 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) //
|
||||
public static function optimize($path) //
|
||||
{
|
||||
$result = [];
|
||||
$result = array();
|
||||
foreach ($path as $n) {
|
||||
switch ($n) {
|
||||
// Может быть относительным или абсолютным путем
|
||||
case "": case ".": break;
|
||||
case "": break;
|
||||
case ".": break;
|
||||
case "..":
|
||||
if (count($result) > 0 && $result[count($result) - 1] != '..') {
|
||||
array_pop($result); break;
|
||||
if (count($result) > 0 && $result[count($result) - 1] != '..') {
|
||||
array_pop($result); break;
|
||||
}
|
||||
default:
|
||||
$result[] = $n;
|
||||
array_push($result, $n);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сравнение двух путей на равентство
|
||||
*/
|
||||
public function equal(Path $path): bool
|
||||
// Сравнение двух путей на равентство
|
||||
public function equal($path/*: Path*/)
|
||||
{
|
||||
$count = count($this->path);
|
||||
if ($count == count($path->path)) {
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
if (count($this->path) == count($path->path)) {
|
||||
for ($i = 0; $i < count($this->path); $i++) {
|
||||
if ($this->path[$i] != $path->path[$i]) {
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -168,29 +171,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'] : '') . '@' : '';
|
||||
|
||||
$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;
|
||||
$slash = (isset($path['host']) && (strlen($path['path']) > 0) && ($path['path'][0] != '/')) ? '/' : '';
|
||||
|
||||
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,16 +201,17 @@ 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'])
|
||||
if (isset($this->url['host']) && isset($path->url['host'])
|
||||
&& ($this->url['host'] != $path->url['host'])) return false;
|
||||
|
||||
$count = count($this->path);
|
||||
if (count($path->path) > $count) {
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
if (count($path->path) > count($this->path)) {
|
||||
for ($i = 0; $i < count($this->path); $i++) {
|
||||
if ($path->path[$i] != $this->path[$i]) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -219,16 +221,17 @@ 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));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Находит путь относительно текущего путя
|
||||
*
|
||||
* @param string $name Полный путь к файлу
|
||||
*
|
||||
* @param string $name Полный путь к файлу
|
||||
* @return string Относительный путь к файлу
|
||||
*/
|
||||
public function relPath($name)
|
||||
|
|
@ -243,12 +246,7 @@ class Path
|
|||
return $path->__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет относительный путь в виде строки
|
||||
* @param string $rpath Путь относительно которого вычисляется относительный путь
|
||||
* @param string $lpath Путь к которому вычисляется относительный путь
|
||||
* @return string Относительный путь
|
||||
*/
|
||||
// Вычисляет относительный путь в виде строки
|
||||
static function relative($rpath, $lpath) {
|
||||
// Нужно проверять диск!!
|
||||
$self = new Path($rpath);
|
||||
|
|
@ -256,28 +254,21 @@ class Path
|
|||
$self_path = $self->getParts();
|
||||
$list_path = $list->getParts();
|
||||
|
||||
$result = [];
|
||||
$count = count($list_path);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
if (($i >= count($self_path)) || $list_path[$i] != $self_path[$i]) {
|
||||
break;
|
||||
$result = array();
|
||||
for ($i = 0; $i < count($list_path); $i++) {
|
||||
if (($i >= count($self_path)) || $list_path[$i] != $self_path[$i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$list_count = count($list_path);
|
||||
for($j = $i; $j < $list_count; $j++) {
|
||||
}
|
||||
for($j = $i; $j < count($list_path); $j++) {
|
||||
array_push($result, '..');
|
||||
}
|
||||
$self_count = count($self_path);
|
||||
for($j = $i; $j < $self_count; $j++) {
|
||||
for($j = $i; $j < count($self_path); $j++) {
|
||||
array_push($result, $self_path[$j]);
|
||||
}
|
||||
return implode("/", $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function append($path)
|
||||
{
|
||||
$base = $this->__toString();
|
||||
|
|
@ -287,11 +278,11 @@ class Path
|
|||
/**
|
||||
* Обьединяет строки в Path соединяя необходимым разделителем
|
||||
* fixme не обрабатывает параметры урла, решение Path::join(SITE_WWW_PATH) . '?param=pampam'
|
||||
* @param string ...$args
|
||||
* @return string
|
||||
* @return string
|
||||
*/
|
||||
static function fromJoin(...$args) {
|
||||
$result = [];
|
||||
static function fromJoin($_rest) {
|
||||
$args = func_get_args();
|
||||
$result = array();
|
||||
$parts0 = new Path(array_shift($args));
|
||||
$result [] = $parts0->getParts();
|
||||
foreach ($args as $file) {
|
||||
|
|
@ -300,7 +291,7 @@ class Path
|
|||
}
|
||||
|
||||
// При обьединении ссылок можно обьеденить path, query, fragment
|
||||
$path = implode(self::SEPARATOR, self::optimize(call_user_func_array('array_merge', $result)));
|
||||
$path = implode(self::SEPARATOR, self::optimize(call_user_func_array('array_merge', $result)));
|
||||
$parts0->url['path'] = ($parts0->isAbsolute()) ? '/' . $path : $path;
|
||||
return $parts0;
|
||||
}
|
||||
|
|
@ -308,33 +299,32 @@ class Path
|
|||
/**
|
||||
* Обьединяет строки в строку пути соединяя необходимым разделителем
|
||||
* fixme не обрабатывает параметры урла, решение Path::join(SITE_WWW_PATH) . '?param=pampam'
|
||||
* @param string ...$args
|
||||
* @return string
|
||||
* @return string
|
||||
*/
|
||||
static function join(...$args)
|
||||
static function join($_rest)
|
||||
{
|
||||
$path = call_user_func_array([self::class, "fromJoin"], $args);
|
||||
$args = func_get_args();
|
||||
$path = call_user_func_array("self::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;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < strlen($name); $i++) {
|
||||
if (!self::isCharName($name[$i])) {
|
||||
return false;
|
||||
|
|
@ -343,7 +333,7 @@ class Path
|
|||
return true;
|
||||
}
|
||||
|
||||
public function isAbsolute(): bool
|
||||
public function isAbsolute()
|
||||
{
|
||||
return $this->absolute;
|
||||
}
|
||||
|
|
@ -352,6 +342,7 @@ class Path
|
|||
* Подбирает новое временное имя для файла
|
||||
*
|
||||
* @param string $dst Предпологаемое имя файла
|
||||
*
|
||||
* @return string Новое имя файла
|
||||
*/
|
||||
static function resolveFile($dst)
|
||||
|
|
@ -368,49 +359,41 @@ 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 = array())
|
||||
{
|
||||
$ignore = array_merge([".", ".."], $ignore);
|
||||
$ignore = array_merge(array(".", ".."), $ignore);
|
||||
return self::fileList($this->__toString(), $allow, $ignore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обьединяет строки в путь соединяя необходимым разделителем
|
||||
*
|
||||
* @param array $allow массив расширений разрешеных для файлов
|
||||
* @param array $ignore массив имен пааок которые не нужно обрабатывать
|
||||
*
|
||||
* @param ?string[] $allow массив расширений разрешеных для файлов
|
||||
* @param string[] $ignore массив имен папок которые не нужно обрабатывать
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
function getContentRec(?array $allow = null, array $ignore = [])
|
||||
* @return array
|
||||
*/
|
||||
function getContentRec($allow = null, $ignore = array())
|
||||
{
|
||||
$result = [];
|
||||
$ignore = array_merge([".", ".."], $ignore);
|
||||
$result = array ();
|
||||
$ignore = array_merge(array (".", ".."), $ignore);
|
||||
self::fileListAll($result, $this->__toString(), $allow, $ignore);
|
||||
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 = [];
|
||||
$result = array ();
|
||||
$handle = opendir($base);
|
||||
if (is_resource($handle)) {
|
||||
while (true) {
|
||||
|
|
@ -419,7 +402,7 @@ class Path
|
|||
if (! in_array($file, $ignore)) {
|
||||
$isDir = is_dir(Path::join($base, $file));
|
||||
if ($isDir || ($allow == null) || in_array(self::getExtension($file), $allow)) {
|
||||
$result[] = $file;
|
||||
$result[] = $file;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
|
|
@ -431,8 +414,8 @@ class Path
|
|||
// При обьединении ссылок можно обьеденить path, query, fragment
|
||||
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 +435,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);
|
||||
|
|
@ -466,29 +449,29 @@ class Path
|
|||
|
||||
/**
|
||||
* Обновить относительную ссылку при переносе файла
|
||||
*
|
||||
* @param string $relativePath - относительная ссылка до переноса
|
||||
* @param string $srcFile - абсолютный путь к папке содержащей файл со ссылкой до переноса
|
||||
* @param string $dstFile - абсолютный путь к папке содержащей файл со ссылкой после переноса
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @param String $relativePath - относительная ссылка до переноса
|
||||
* @param String $srcFile - абсолютный путь к папке содержащей файл со ссылкой до переноса
|
||||
* @param String $dstFile - абсолютный путь к папке содержащей файл со ссылкой после переноса
|
||||
*
|
||||
* @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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить относительную ссылку в файле при переносе папки
|
||||
*
|
||||
* @param string $relativePath относительная ссылка до переноса
|
||||
* @param string $fileDir абсолютный путь к директории файла содержащего ссылку до переноса
|
||||
* @param string $srcDir абсолютный путь к переносимой директории до переноса
|
||||
* @param string $dstDir абсолютный путь к переносимой директории после переноса
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @param String $relativePath - относительная ссылка до переноса
|
||||
* @param String $fileDir - абсолютный путь к директории файла содержащего ссылку до переноса
|
||||
* @param String $srcDir - абсолютный путь к переносимой директории до переноса
|
||||
* @param String $dstDir - абсолютный путь к переносимой директории после переноса
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -3,117 +3,80 @@
|
|||
/**
|
||||
* Преобразование типов !!! Пересмотреть использование классов!!
|
||||
* Класс преобразования типа значения поля класса в тип поля таблицы
|
||||
* @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);
|
||||
$tmp = explode("/", $value, 3);
|
||||
if (!empty($tmp)) {
|
||||
if (count($tmp) != 3) return $result;
|
||||
|
||||
if (count($tmp) != 3) {
|
||||
return $result;
|
||||
}
|
||||
$year = intval($tmp[2]);
|
||||
$month = intval($tmp[1]);
|
||||
$day = intval($tmp[0]);
|
||||
|
||||
$year = (int)$tmp[2];
|
||||
$month = (int)$tmp[1];
|
||||
$day = (int)$tmp[0];
|
||||
|
||||
if ($month != 0 && $day != 0 && $year != 0) {
|
||||
if (checkdate($month, $day, $year)) {
|
||||
return mktime(0, 0, 0, $month, $day, $year) ?: 0;
|
||||
} else {
|
||||
return 0;
|
||||
if ($month != 0 && $day != 0 && $year != 0) {
|
||||
if (checkdate($month, $day, $year)) {
|
||||
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 = [];
|
||||
$tmp = array();
|
||||
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;
|
||||
if (checkdate($tmp[2], $tmp[3], $tmp[1])) {
|
||||
$result = mktime($tmp[4], $tmp[5], 0, $tmp[2], $tmp[3], $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 +84,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 +93,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 : [];
|
||||
return (is_array($value)) ? $value : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @template T
|
||||
* @param T $value
|
||||
* @return T
|
||||
*/
|
||||
public static function from_array($value)
|
||||
{
|
||||
return $value;
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* process_exists
|
||||
* @param int $pid
|
||||
* @return bool
|
||||
*/
|
||||
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) {
|
||||
if ($process != "") {
|
||||
$csv = str_getcsv($process);
|
||||
if ($csv && $pid == $csv[1]) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return file_exists("/proc/$pid");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* create_single_proces
|
||||
* @param string $fpid
|
||||
* @param callable $fn
|
||||
* @return int
|
||||
*/
|
||||
function create_single_process($fpid, $fn)
|
||||
{
|
||||
if (file_exists($fpid)) {
|
||||
if (process_exists((int)file_get_contents($fpid))) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
call_user_func($fn);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,62 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso;
|
||||
use ctiso\File,
|
||||
Exception;
|
||||
///<reference path="Settings.php" />
|
||||
|
||||
class Registry {
|
||||
/** @var array<string, array{path: string, data: mixed}> */
|
||||
private array $namespace = [];
|
||||
/**
|
||||
* http://www.patternsforphp.com/wiki/Registry
|
||||
* http://www.patternsforphp.com/wiki/Singleton
|
||||
* http://www.phppatterns.com/docs/design/the_registry?s=registry
|
||||
*/
|
||||
|
||||
function importFile(string $namespace, ?string $filePath = null): void {
|
||||
$data = json_decode(File::getContents($filePath), true);
|
||||
$data['_file'] = $filePath;
|
||||
$this->namespace[$namespace] = [
|
||||
'path' => $filePath,
|
||||
'data' => $data
|
||||
];
|
||||
}
|
||||
|
||||
function importArray(string $namespace, array $data = []): void {
|
||||
if (isset($this->namespace[$namespace])) {
|
||||
$data = array_merge($this->namespace[$namespace]['data'], $data);
|
||||
class Registry extends Settings
|
||||
{
|
||||
static $instance = null;
|
||||
|
||||
/**
|
||||
*/
|
||||
static public function getInstance()
|
||||
{
|
||||
if (self::$instance == null) {
|
||||
self::$instance = new Registry();
|
||||
}
|
||||
$this->namespace[$namespace] = [
|
||||
'path' => null,
|
||||
'data' => $data
|
||||
];
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ns
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
* Список модулей
|
||||
*/
|
||||
public function get(string $ns, string $key) {
|
||||
if (isset($this->namespace[$ns]['data'][$key])) {
|
||||
return $this->namespace[$ns]['data'][$key];
|
||||
}
|
||||
throw new Exception('Unknown key ' . $ns . '::' . $key);
|
||||
public function getModules()
|
||||
{
|
||||
return array_keys($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ns
|
||||
* @param string $key
|
||||
* @return mixed|null
|
||||
* Проверка наличия модуля
|
||||
*/
|
||||
public function getOpt(string $ns, string $key) {
|
||||
if (isset($this->namespace[$ns]['data'][$key])) {
|
||||
return $this->namespace[$ns]['data'][$key];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function has(string $ns, string $key): bool {
|
||||
return isset($this->namespace[$ns]['data'][$key]);
|
||||
}
|
||||
|
||||
function set(string $ns, string $key, mixed $value): void {
|
||||
$this->namespace[$ns]['data'][$key] = $value;
|
||||
public function hasModule($name)
|
||||
{
|
||||
return isset($this->data[$name]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Role;
|
||||
|
||||
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 $access;
|
||||
public string $password;
|
||||
/** @var int */
|
||||
public $id;
|
||||
public Database $db;
|
||||
public array $groups;
|
||||
|
||||
function __construct(Database $db, array $groups) {
|
||||
$this->db = $db;
|
||||
$this->groups = $groups;
|
||||
}
|
||||
|
||||
public function setDB(Database $db): void {
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
function isLogged() {
|
||||
return \ctiso\Filter\Authorization::isLogged();
|
||||
}
|
||||
|
||||
public function getUserByQuery(Statement $stmt): ?PDOStatement
|
||||
{
|
||||
$result = $stmt->executeQuery();
|
||||
if ($result->next()) {
|
||||
$this->access = $this->groups[$result->getString('access')];
|
||||
$this->name = $result->getString('login');
|
||||
$this->id = $result->getInt('id_user');
|
||||
$this->password = $result->getString('password');
|
||||
$this->fullname = implode(' ', [
|
||||
$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
|
||||
{
|
||||
$stmt = $this->db->prepareStatement("SELECT * FROM users WHERE login = ?");
|
||||
$stmt->setString(1, $login);
|
||||
$result = $this->getUserByQuery($stmt);
|
||||
if ($result) {
|
||||
$time = time();
|
||||
$id = $this->id;
|
||||
$this->db->executeQuery(
|
||||
"UPDATE users SET lasttime = :time WHERE id_user = :id",
|
||||
['time' => $time, 'id' => $id]); // Время входа
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getUserById(int $id): ?PDOStatement
|
||||
{
|
||||
$stmt = $this->db->prepareStatement("SELECT * FROM users WHERE id_user = ?");
|
||||
$stmt->setInt(1, $_SESSION ['access']);
|
||||
$result = $this->getUserByQuery($stmt);
|
||||
if ($result) {
|
||||
$lasttime = $result->getInt('lasttime');
|
||||
$time = time();
|
||||
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 resetTries(string $login): void {
|
||||
$this->db->executeQuery(
|
||||
"UPDATE users SET trie_count = :count WHERE login = :login",
|
||||
['count' => 0, 'login' => $login]
|
||||
);
|
||||
}
|
||||
|
||||
function updateTries(string $login): void {
|
||||
$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']]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
<?php
|
||||
|
||||
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) {
|
||||
|
|
@ -32,4 +30,8 @@ class Security {
|
|||
}
|
||||
return $password;
|
||||
}
|
||||
|
||||
public static function generateAPIKey($timestamp){
|
||||
return $timestamp."_".md5(WWW_PATH.$timestamp."asjfgkg0o123rkdsdfl1023kwaf012kj10asdld");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso;
|
||||
|
||||
class Session
|
||||
class Session
|
||||
{
|
||||
function get(string $key): mixed
|
||||
function get($key)
|
||||
{
|
||||
if (isset($_SESSION[$key])) {
|
||||
return $_SESSION[$key];
|
||||
|
|
@ -12,27 +10,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();
|
||||
}
|
||||
|
|
|
|||
163
src/Settings.php
163
src/Settings.php
|
|
@ -1,40 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso;
|
||||
use ctiso\File,
|
||||
Exception;
|
||||
|
||||
/**
|
||||
* Класс реестра
|
||||
* Класс реестра
|
||||
* Реестр организован как ассоциативный многомерный массив
|
||||
* array( 'name1' => parameters1, 'name2' => parameters1, ... )
|
||||
*
|
||||
* name1, name2 ... - Имена модулей
|
||||
* name1, name2 ... - Имена модулей
|
||||
* parameters1, parameters1 - Массивы с параметрами модуля
|
||||
* Имя необходимо чтобы потом легко было удалить ненужные ветки дерева
|
||||
*/
|
||||
class Settings
|
||||
class Settings extends Collection
|
||||
{
|
||||
/** @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'];
|
||||
$extname = pathinfo($file, PATHINFO_EXTENSION);
|
||||
|
||||
$this->format = $format ?: Arr::get($fileFormat, $extname, $extname);
|
||||
$this->format = $format ? $format : Arr::get($fileFormat, $extname, $extname);
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
|
|
@ -42,48 +27,43 @@ class Settings
|
|||
* Чтение настроек из файла
|
||||
* @return Boolean
|
||||
*/
|
||||
public function read(): bool
|
||||
public function read()
|
||||
{
|
||||
if (!file_exists ($this->file)) {
|
||||
$this->is_read = true;
|
||||
return false;
|
||||
}
|
||||
// Не include_once т.к читать настройки можно несколько раз
|
||||
$settings = [];
|
||||
$settings = array();
|
||||
if ($this->format == 'json') {
|
||||
$settings = json_decode(File::getContents($this->file), true);
|
||||
} else {
|
||||
$settings = include ($this->file);
|
||||
include ($this->file);
|
||||
}
|
||||
|
||||
if (!is_array($settings)) {
|
||||
throw new Exception('no data in ' . $this->file);
|
||||
throw new Exception($this->file);
|
||||
}
|
||||
|
||||
$this->is_read = true;
|
||||
$this->data = $settings;
|
||||
return true;
|
||||
return $this->import($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])) {
|
||||
$data[$name] = [];
|
||||
$data[$name] = array();
|
||||
}
|
||||
$this->merge($data[$name], $value);
|
||||
} else {
|
||||
|
|
@ -93,14 +73,12 @@ 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)) {
|
||||
if (! isset($data[$key])) $data[$key] = [];
|
||||
if (! isset($data[$key])) $data[$key] = array();
|
||||
$this->merge($data[$key], $subvalue);
|
||||
} else {
|
||||
$data[$key] = $subvalue;
|
||||
|
|
@ -109,23 +87,17 @@ class Settings
|
|||
}
|
||||
|
||||
/**
|
||||
* Чтение ключа из реестра
|
||||
* @param array $key Путь к значению ключа
|
||||
* @return mixed
|
||||
* Чтение ключа из реестра
|
||||
* @param $args Путь к значению ключа
|
||||
*/
|
||||
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 +105,9 @@ class Settings
|
|||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// assert(count($key) == 1);
|
||||
$name = array_shift($key);
|
||||
if (isset($data[$name])) {
|
||||
return $data[$name];
|
||||
|
|
@ -145,12 +118,12 @@ class Settings
|
|||
|
||||
/**
|
||||
* Чтение ключа из реестра (Собирает все ключи с определенным значением во всех модулях)
|
||||
* @param mixed $key Путь к значению ключа внутри модуля
|
||||
* @return array
|
||||
* @param $key Путь к значению ключа внутри модуля
|
||||
*/
|
||||
public function readKeyList(...$key)
|
||||
public function readKeyList($_rest)
|
||||
{
|
||||
$result = [];
|
||||
$key = func_get_args();
|
||||
$result = array();
|
||||
foreach ($this->data as $name => $value) {
|
||||
$output = $this->readKeyData($key, $value);
|
||||
if ($output) {
|
||||
|
|
@ -160,17 +133,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) {
|
||||
|
|
@ -181,80 +150,26 @@ class Settings
|
|||
unset($data[$name]);
|
||||
}
|
||||
|
||||
public function getOwner()
|
||||
{
|
||||
return array_keys($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись настроек в файл (Может переименовать в store)
|
||||
*
|
||||
* @param string $file
|
||||
* @param File $file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function write($file = null)
|
||||
{
|
||||
if (!$this->is_read) {
|
||||
throw new Exception('read settings before write');
|
||||
}
|
||||
|
||||
if ($this->format == 'json') {
|
||||
$result = json_encode($this->data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
} else {
|
||||
$result = var_export($this->data, true);
|
||||
$result = "<?php\nreturn ".$result.";\n?>";
|
||||
$result = "<?php\n\$settings = ".$result.";\n?>";
|
||||
}
|
||||
file_put_contents(($file) ? $file : $this->file, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка значения ключа
|
||||
* @param string $key Ключ
|
||||
* @param mixed $value Значение
|
||||
*/
|
||||
public function set($key, $value): void {
|
||||
$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()
|
||||
{
|
||||
return array_keys($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка наличия ключа
|
||||
* @param string $name Ключ
|
||||
* @return bool
|
||||
*/
|
||||
public function hasKey($name)
|
||||
{
|
||||
return isset($this->data[$name]);
|
||||
file_put_contents (($file) ? $file : $this->file, $result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
182
src/Setup.php
182
src/Setup.php
|
|
@ -6,104 +6,58 @@
|
|||
* $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 {
|
||||
$this->base = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает содержимое файла
|
||||
* @param string $file
|
||||
* @return string
|
||||
*/
|
||||
function getFromName($file) {
|
||||
return File::getContents(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 = '';
|
||||
$this->zip = new FakeZipArchive();
|
||||
$this->zip->open(dirname($this->file) . '/' . $this->node->attributes()['dir']);
|
||||
$this->zip = new ZipArchive();
|
||||
$this->zip->open(strtr($file, array('.xml' => '.zip')));
|
||||
|
||||
|
||||
array_push($this->stack, $this->node);
|
||||
|
||||
$this->registerAction('copy', [$this, 'copyFile']);
|
||||
$this->registerAction('make-directory', [$this, 'makeDirectory']);
|
||||
$this->registerAction('make-link', [$this, 'makeLink']);
|
||||
$this->registerAction('include', [$this, 'includeFile']);
|
||||
$this->registerAction('when', [$this, 'testWhen']);
|
||||
$this->registerAction('copy', array($this, 'copyFile'));
|
||||
$this->registerAction('make-directory', array($this, 'makeDirectory'));
|
||||
$this->registerAction('make-link', array($this, 'makeLink'));
|
||||
$this->registerAction('include', array($this, 'includeFile'));
|
||||
$this->registerAction('when', array($this, 'testWhen'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрация новых действия для установки
|
||||
* @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 +70,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);
|
||||
$result = preg_replace_callback('/\{\{\s*(\*?)(\w+)\s*\}\}/', array($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 +86,6 @@ class Setup
|
|||
|
||||
/**
|
||||
* Заменяет переменные на их значения в строке
|
||||
* @param array<string> $match массив совпадения
|
||||
* @return string
|
||||
*/
|
||||
function replaceVariable(array $match)
|
||||
{
|
||||
|
|
@ -145,55 +97,48 @@ class Setup
|
|||
|
||||
/**
|
||||
* Для всех аттрибутов заменяет переменные на их значения
|
||||
* @param SimpleXMLElement $attributes аттрибуты
|
||||
* @return array<string, string>
|
||||
*/
|
||||
function resolve(SimpleXMLElement $attributes): array
|
||||
{
|
||||
$result = [];
|
||||
function resolve($attributes)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($attributes as $key => $value) {
|
||||
$result[$key] = preg_replace_callback("/\\\${(\w+)}/", $this->replaceVariable(...), $value);
|
||||
$result [$key] = preg_replace_callback("/\\\${(\w+)}/", array($this, 'replaceVariable'), $value);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Выполняет список действий если для действия не указан аттрибут when то оно выполняется всегда
|
||||
*
|
||||
* @param string $action специальное действие
|
||||
* @return void
|
||||
*
|
||||
* @param $action специальное действие
|
||||
*/
|
||||
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)]);
|
||||
$this->callAction($node->getName(), array($this->resolve($attributes)));
|
||||
array_pop($this->stack);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирования файла
|
||||
* preserve - Не переписывать файл если он существует
|
||||
* template Файл является шаблоном подставить параметры до копирования
|
||||
* src Исходный файл
|
||||
* dst Новый файл
|
||||
*
|
||||
* @param array{preserve?: string, template: string, src: string, dst: string} $attributes
|
||||
* @return void
|
||||
* Копирования файла
|
||||
* @param preserve Не переписывать файл если он существует
|
||||
* @param template Файл является шаблоном подставить параметры до копирования
|
||||
* @param src Исходный файл
|
||||
* @param dst Новый файл
|
||||
*/
|
||||
public function copyFile(array $attributes)
|
||||
{
|
||||
{
|
||||
$path = $this->targetPath($attributes['dst']);
|
||||
|
||||
if (!(file_exists($path) && isset($attributes['preserve']))) {
|
||||
|
|
@ -203,9 +148,9 @@ class Setup
|
|||
|
||||
/**
|
||||
* Создает символическую ссылку на папку/файл
|
||||
* @param array{target: string, link: string} $attributes
|
||||
* @param dst Имя папки
|
||||
*/
|
||||
public function makeLink(array $attributes): void
|
||||
public function makeLink(array $attributes)
|
||||
{
|
||||
if (function_exists('symlink')) {
|
||||
symlink($attributes['target'], $attributes['link']);
|
||||
|
|
@ -213,37 +158,35 @@ class Setup
|
|||
}
|
||||
|
||||
/**
|
||||
* Подключение файла установки
|
||||
* @param array{file: string} $attributes Имя подключаемого файла
|
||||
* Подключение файла установки
|
||||
* @param file Имя подключаемого файла
|
||||
*/
|
||||
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
|
||||
* @param dst Имя папки
|
||||
*/
|
||||
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,26 +194,19 @@ 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));
|
||||
$stmtList = Tools_SQLStatementExtractor::extract($this->zip->getFromName($file));
|
||||
foreach ($stmtList as $stmt) {
|
||||
$conn->executeQuery ($stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполнение Списка SQL команд
|
||||
* @param Database $conn
|
||||
* @param string $file
|
||||
*/
|
||||
static function batchSQL($conn, $file): void
|
||||
static function batchSQL($conn/*: Database*/, $file)
|
||||
{
|
||||
$stmtList = SQLStatementExtractor::extractFile($file);
|
||||
$stmtList = Tools_SQLStatementExtractor::extractFile($file);
|
||||
foreach ($stmtList as $stmt) {
|
||||
$conn->executeQuery ($stmt);
|
||||
}
|
||||
|
|
|
|||
66
src/Shortcut.php
Normal file
66
src/Shortcut.php
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Класс для короткого доступа к файлам / папкам
|
||||
*/
|
||||
class Shortcut
|
||||
{
|
||||
static $instance = null;
|
||||
public $variables = array();
|
||||
public $list = array();
|
||||
|
||||
// Singleton pattern
|
||||
static public function getInstance()
|
||||
{
|
||||
if (self::$instance == null) {
|
||||
self::$instance = new Shortcut();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет ярлык с именем $prefix
|
||||
* Путь может содержать переменные
|
||||
*/
|
||||
public function addUrl($prefix, $path)
|
||||
{
|
||||
$this->list[$prefix] = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function addVar($name, $value)
|
||||
{
|
||||
$this->variables['$' . $name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает путь по имени ярлыка
|
||||
*/
|
||||
static function getUrl($prefix, $name = null, $name1 = null)
|
||||
{
|
||||
$shortcut = self::getInstance();
|
||||
|
||||
$names = $shortcut->variables;
|
||||
if ($name) {
|
||||
$names['$name'] = $name;
|
||||
}
|
||||
if ($name1) {
|
||||
$names['$name1'] = $name1;
|
||||
}
|
||||
|
||||
if (isset($shortcut->list[$prefix])) {
|
||||
return strtr($shortcut->list[$prefix], $names);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static function expand($path)
|
||||
{
|
||||
$shortcut = self::getInstance();
|
||||
$names = $shortcut->variables;
|
||||
return strtr($path, $names);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,24 +1,19 @@
|
|||
<?php
|
||||
|
||||
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 +21,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,23 +29,23 @@ 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']);
|
||||
return usort($list, array($this, 'compare'));
|
||||
}
|
||||
|
||||
function sortKeys(array &$list): bool
|
||||
function sortKeys(&$list)
|
||||
{
|
||||
return usort($list, [$this, 'compareKeys']);
|
||||
return usort($list, array($this, 'compareKeys'));
|
||||
}
|
||||
|
||||
function group(array &$list, string $key, array $types): void
|
||||
function group(&$list, $key, $types)
|
||||
{
|
||||
$groups = [];
|
||||
$groups = array();
|
||||
foreach ($types as $name) {
|
||||
$groups[$name] = [];
|
||||
$groups[$name] = array();
|
||||
}
|
||||
$groups['_any_'] = [];
|
||||
$groups['_any_'] = array();
|
||||
foreach ($list as $item) {
|
||||
if (isset($groups[$item[$key]])) {
|
||||
$groups[$item[$key]][] = $item;
|
||||
|
|
@ -58,10 +53,10 @@ class SortRecord
|
|||
$groups['_any_'][] = $item;
|
||||
}
|
||||
}
|
||||
$result = [];
|
||||
$result = array();
|
||||
foreach ($groups as $value) {
|
||||
$result = array_merge($result, $value);
|
||||
}
|
||||
$list = $result;
|
||||
$list = $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Преобразование дерева из модели Plain в массив массивов (Adjacency List)
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
$name = array_shift ($level);
|
||||
|
||||
$keys = Functions::key_unique_values($name, $table);
|
||||
$data = [];
|
||||
foreach ($keys as $index) {
|
||||
list($rows, $table) = Functions::partition (Functions::lcurry(['\ctiso\Functions', '__index'], $index, $name), $table);
|
||||
// $rows = array_filter ($table, lcurry('__index', intval($index), $name));
|
||||
//$rows = array_filter ($table, create_function ('$x', 'return __index ('.intval($index).', \''.$name.'\', $x);'));
|
||||
$data[$index] = call_user_func ($fn, $name, $index, $rows, self::walk ($level, $rows, $fn));
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Расширения для PHPTAL для отображения времени и даты
|
||||
*/
|
||||
namespace ctiso;
|
||||
|
||||
use PHPTAL_Php_TalesInternal;
|
||||
use ctiso\Controller\SiteInterface;
|
||||
use ctiso\Controller\Component;
|
||||
use ctiso\HttpRequest;
|
||||
use PHPTAL_Tales;
|
||||
use PHPTAL_TalesRegistry;
|
||||
|
||||
class Tales_DateTime implements PHPTAL_Tales
|
||||
{
|
||||
static public function date(string $expression, bool $nothrow = false): string
|
||||
{
|
||||
return "ctiso\\Tales::phptal_date(".PHPTAL_Php_TalesInternal::path($expression).")";
|
||||
}
|
||||
|
||||
static public function time(string $expression, bool $nothrow = false): string
|
||||
{
|
||||
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
|
||||
{
|
||||
$s = PHPTAL_Php_TalesInternal::string($expression);
|
||||
return "ctiso\\Tales::phptal_component(" . $s . ")";
|
||||
}
|
||||
}
|
||||
|
||||
class Tales_Assets implements PHPTAL_Tales
|
||||
{
|
||||
static public function assets(string $expression, bool $nothrow = false): string
|
||||
{
|
||||
$s = PHPTAL_Php_TalesInternal::string($expression);
|
||||
return "ctiso\\Tales::phptal_asset(" . $s . ")";
|
||||
}
|
||||
}
|
||||
|
||||
class Tales {
|
||||
/** @var ?SiteInterface */
|
||||
static $site;
|
||||
|
||||
static function phptal_date (int $e): string {
|
||||
return date("d.m.Y", $e);
|
||||
}
|
||||
|
||||
static function phptal_time (int $e): string {
|
||||
return date("H:i", $e);
|
||||
}
|
||||
|
||||
static function phptal_asset(string $s): string {
|
||||
self::$site->addStyleSheet($s);
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Функция подключения компонента
|
||||
* @param string $expression
|
||||
* @return string
|
||||
*/
|
||||
static function phptal_component($expression): string {
|
||||
$begin = floatval(microtime(true));
|
||||
/** @var Component */
|
||||
$component = self::$site->loadComponent($expression);
|
||||
$req = new HttpRequest();
|
||||
$result = $component->execute($req);
|
||||
|
||||
echo "<!-- ", $expression, ", ", round(floatval(microtime(true)) - $begin, 4), "sec -->";
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
static function register(?SiteInterface $site): void {
|
||||
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']);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Tools;
|
||||
|
||||
use GdImage;
|
||||
|
||||
class Drawing
|
||||
class Tools_Drawing
|
||||
{
|
||||
const ALIGN_LEFT = "left";
|
||||
const ALIGN_TOP = "top";
|
||||
const ALIGN_BOTTOM = "bottom";
|
||||
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
|
||||
* http://ru2.php.net/imagettftext
|
||||
*/
|
||||
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 = [];
|
||||
$line_widths = [];
|
||||
|
||||
$largest_line_height = 0;
|
||||
|
||||
$lines = array();
|
||||
$line_widths = array();
|
||||
|
||||
$largest_line_height = 0;
|
||||
foreach ($text_lines as $block) {
|
||||
$current_line = ''; // Reset current line
|
||||
$words = explode(' ', $block); // Split the text into an array of single words
|
||||
$current_line = ''; // Reset current line
|
||||
$words = explode(' ', $block); // Split the text into an array of single words
|
||||
$first_word = true;
|
||||
|
||||
$last_width = 0;
|
||||
$count = count($words);
|
||||
$item = '';
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
|
||||
$last_width = 0;
|
||||
for ($i = 0; $i < count($words); $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];
|
||||
|
||||
|
||||
$largest_line_height = max($line_height, $largest_line_height);
|
||||
|
||||
|
||||
if ($line_width > $max_width && !$first_word) {
|
||||
$lines[] = $current_line;
|
||||
|
||||
$line_widths[] = $last_width ?: $line_width;
|
||||
|
||||
$line_widths[] = $last_width ? $last_width : $line_width;
|
||||
$current_line = $item;
|
||||
} else {
|
||||
$current_line .= ($first_word ? '' : ' ') . $item;
|
||||
}
|
||||
|
||||
|
||||
if ($i == count($words) - 1) {
|
||||
$lines[] = $current_line;
|
||||
$lines[] = $current_line;
|
||||
$line_widths[] = $line_width;
|
||||
}
|
||||
|
||||
$last_width = $line_width;
|
||||
|
||||
$last_width = $line_width;
|
||||
$first_word = false;
|
||||
}
|
||||
|
||||
|
||||
if ($current_line) {
|
||||
$current_line = $item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// vertical align
|
||||
$top_offset = 0;
|
||||
if ($valign == self::ALIGN_CENTER) {
|
||||
$top_offset = ($max_height - $largest_line_height * count($lines)) / 2;
|
||||
} elseif ($valign == self::ALIGN_BOTTOM) {
|
||||
$top_offset = $max_height - $largest_line_height * count($lines);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$top += $largest_line_height + $top_offset;
|
||||
|
||||
|
||||
$i = 0;
|
||||
foreach ($lines as $line) {
|
||||
// horizontal align
|
||||
|
|
@ -134,23 +90,28 @@ class Drawing
|
|||
} elseif ($align == self::ALIGN_RIGHT) {
|
||||
$left_offset = ($max_width - $line_widths[$i]);
|
||||
}
|
||||
|
||||
|
||||
imagettftext($image, $size, $angle, $left + $left_offset, $top + ($largest_line_height * $i), $color, $font, $line);
|
||||
$i++;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
if ($spacing == 0) {
|
||||
|
||||
function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0)
|
||||
{
|
||||
if ($spacing == 0)
|
||||
{
|
||||
imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
|
||||
} else {
|
||||
}
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Tools;
|
||||
|
||||
use GdImage;
|
||||
|
||||
class Image
|
||||
class Tools_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 +10,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 +35,5 @@ class Image
|
|||
case 'png': imagepng($image, $uri); break;
|
||||
case 'gif': imagegif($image, $uri); break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -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,139 @@
|
|||
* @version $Revision: 1.5 $
|
||||
* @package creole.util.sql
|
||||
*/
|
||||
|
||||
namespace ctiso\Tools;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SQLStatementExtractor
|
||||
{
|
||||
|
||||
/** @var string */
|
||||
class Tools_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
|
||||
*
|
||||
* @param string $txt
|
||||
* @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 string
|
||||
*/
|
||||
protected static function extractStatements($lines)
|
||||
{
|
||||
|
||||
$statements = [];
|
||||
protected static function extractStatements($lines) {
|
||||
|
||||
$statements = array();
|
||||
$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.
|
||||
*
|
||||
* @param string $filename
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
110
src/Tools/String.php
Normal file
110
src/Tools/String.php
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
class Tools_String {
|
||||
|
||||
// from creole
|
||||
static function strToArray($str) {
|
||||
$str = substr($str, 1, -1); // remove { }
|
||||
$res = array();
|
||||
|
||||
$subarr = array();
|
||||
$in_subarr = 0;
|
||||
|
||||
$toks = explode(',', $str);
|
||||
foreach($toks as $tok) {
|
||||
if ($in_subarr > 0) { // already in sub-array?
|
||||
$subarr[$in_subarr][] = $tok;
|
||||
if ('}' === substr($tok, -1, 1)) { // check to see if we just added last component
|
||||
$res[] = static::strToArray(implode(',', $subarr[$in_subarr]));
|
||||
$in_subarr--;
|
||||
}
|
||||
} elseif ($tok[0] === '{') { // we're inside a new sub-array
|
||||
if ('}' !== substr($tok, -1, 1)) {
|
||||
$in_subarr++;
|
||||
// if sub-array has more than one element
|
||||
$subarr[$in_subarr] = array();
|
||||
$subarr[$in_subarr][] = $tok;
|
||||
} else {
|
||||
$res[] = static::strToArray($tok);
|
||||
}
|
||||
} else { // not sub-array
|
||||
$val = trim($tok, '"'); // remove " (surrounding strings)
|
||||
// perform type castng here?
|
||||
$res[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
//Нормализация строк на русском
|
||||
static function normalizeRussian($str) {
|
||||
$result = preg_replace('/\s+/',' ', $str);
|
||||
if (is_string($result)) {
|
||||
$result = trim($result); //Замена длинных пробелов на одинарные, пробелы по краям
|
||||
$result = mb_strtolower($result);
|
||||
$result = preg_replace('/ё/','е', $str); //е на ё
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
//Проверка равенства двух строк на русском языке.
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
return $founded;
|
||||
}
|
||||
|
||||
static function mb_str_split($str) {
|
||||
return preg_split('~~u', $str, null, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
static function mb_strtr($str, $from, $to) {
|
||||
return str_replace(self::mb_str_split($from), self::mb_str_split($to), $str);
|
||||
}
|
||||
|
||||
static function encodestring($st) {
|
||||
$st = self::mb_strtr($st,"абвгдеёзийклмнопрстуфхъыэ !+()", "abvgdeeziyklmnoprstufh_ie_____");
|
||||
$st = self::mb_strtr($st,"АБВГДЕЁЗИЙКЛМНОПРСТУФХЪЫЭ", "ABVGDEEZIYKLMNOPRSTUFH_IE");
|
||||
$st = strtr($st, array(
|
||||
" " => '_',
|
||||
"." => '_',
|
||||
"," => '_',
|
||||
"?" => '_',
|
||||
"\"" => '_',
|
||||
"'" => '_',
|
||||
"/" => '_',
|
||||
"\\" => '_',
|
||||
"%" => '_',
|
||||
"#" => '_',
|
||||
"*" => '_',
|
||||
"ж"=>"zh", "ц"=>"ts", "ч"=>"ch", "ш"=>"sh",
|
||||
"щ"=>"shch","ь"=>"", "ю"=>"yu", "я"=>"ya",
|
||||
"Ж"=>"ZH", "Ц"=>"TS", "Ч"=>"CH", "Ш"=>"SH",
|
||||
"Щ"=>"SHCH","Ь"=>"", "Ю"=>"YU", "Я"=>"YA",
|
||||
"Й"=>"i", "й"=>"ie", "ё"=>"Ye",
|
||||
"№"=>"N"
|
||||
));
|
||||
return strtolower($st);
|
||||
}
|
||||
|
||||
static function validate_encoded_string($st) {
|
||||
$enc_st = self::encodestring($st);
|
||||
return preg_match('/^[\w_-]+(\.[\w_-]+)?$/', $enc_st);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Tools;
|
||||
|
||||
class StringUtil
|
||||
{
|
||||
|
||||
/**
|
||||
* Преобразует строку в массив
|
||||
* @param string $str
|
||||
* @return array
|
||||
*/
|
||||
static function strToArray(string $str): array
|
||||
{
|
||||
$str = substr($str, 1, -1); // remove { }
|
||||
$res = [];
|
||||
|
||||
$subarr = [];
|
||||
$in_subarr = 0;
|
||||
|
||||
$toks = explode(',', $str);
|
||||
foreach ($toks as $tok) {
|
||||
if ($in_subarr > 0) { // already in sub-array?
|
||||
$subarr[$in_subarr][] = $tok;
|
||||
if ('}' === substr($tok, -1, 1)) { // check to see if we just added last component
|
||||
$res[] = static::strToArray(implode(',', $subarr[$in_subarr]));
|
||||
$in_subarr--;
|
||||
}
|
||||
} elseif ($tok[0] === '{') { // we're inside a new sub-array
|
||||
if ('}' !== substr($tok, -1, 1)) {
|
||||
$in_subarr++;
|
||||
// if sub-array has more than one element
|
||||
$subarr[$in_subarr] = [];
|
||||
$subarr[$in_subarr][] = $tok;
|
||||
} else {
|
||||
$res[] = static::strToArray($tok);
|
||||
}
|
||||
} else { // not sub-array
|
||||
$val = trim($tok, '"'); // remove " (surrounding strings)
|
||||
// perform type castng here?
|
||||
$res[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Нормализация строк на русском
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
static function normalizeRussian(string $str): string
|
||||
{
|
||||
$result = preg_replace('/\s+/', ' ', $str);
|
||||
if (is_string($result)) {
|
||||
$result = trim($result); //Замена длинных пробелов на одинарные, пробелы по краям
|
||||
$result = mb_strtolower($result);
|
||||
$result = preg_replace('/ё/', 'е', $str); //е на ё
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка равенства двух строк на русском языке.
|
||||
* @param string $str1
|
||||
* @param string $str2
|
||||
* @return bool
|
||||
*/
|
||||
static function equalRussianCheck($str1, $str2): bool
|
||||
{
|
||||
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)
|
||||
{
|
||||
$variants_array = explode('|', $variants);
|
||||
$founded = false;
|
||||
foreach ($variants_array as $variant) {
|
||||
$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) ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Заменяет символы в строке на символы из другой строки
|
||||
* @param string $str
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @return string
|
||||
*/
|
||||
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");
|
||||
$st = strtr($st, [
|
||||
" " => '_',
|
||||
"." => '_',
|
||||
"," => '_',
|
||||
"?" => '_',
|
||||
"\"" => '_',
|
||||
"'" => '_',
|
||||
"/" => '_',
|
||||
"\\" => '_',
|
||||
"%" => '_',
|
||||
"#" => '_',
|
||||
"*" => '_',
|
||||
"ж" => "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
|
||||
{
|
||||
$enc_st = self::encodestring($st);
|
||||
return preg_match('/^[\w_-]+(\.[\w_-]+)?$/', $enc_st);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,18 +3,10 @@
|
|||
/**
|
||||
* Формат для композиции изображений
|
||||
*/
|
||||
|
||||
namespace ctiso\Tools;
|
||||
|
||||
use ctiso\Tools\Drawing;
|
||||
use GdImage;
|
||||
|
||||
class TemplateImage
|
||||
class Tools_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,29 +21,27 @@ 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 $_prepare = true;
|
||||
/** @var bool */
|
||||
protected $context = array();
|
||||
protected $data = array();
|
||||
protected $base = "c:\\windows\\fonts\\";
|
||||
protected $image;
|
||||
protected $prepare = true;
|
||||
public $debug = false;
|
||||
public string $resource;
|
||||
public string $filename;
|
||||
public $filename;
|
||||
public $resource;
|
||||
|
||||
function __construct(?array $template = null)
|
||||
function __construct ($template = false)
|
||||
{
|
||||
if ($template) {
|
||||
// assert(is_string($src));
|
||||
if($template) {
|
||||
$this->data = $template;
|
||||
}
|
||||
}
|
||||
|
|
@ -59,166 +49,132 @@ 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]) {
|
||||
$align = Drawing::ALIGN_LEFT;
|
||||
$align = Tools_Drawing::ALIGN_LEFT;
|
||||
} elseif ($value->align[2]) {
|
||||
$align = Drawing::ALIGN_RIGHT;
|
||||
$align = Tools_Drawing::ALIGN_RIGHT;
|
||||
} else {
|
||||
$align = Drawing::ALIGN_CENTER;
|
||||
$align = Tools_Drawing::ALIGN_CENTER;
|
||||
}
|
||||
|
||||
if ($value->valign[0]) {
|
||||
$valign = Drawing::ALIGN_TOP;
|
||||
} elseif ($value->valign[1]) {
|
||||
$valign = Drawing::ALIGN_CENTER;
|
||||
$valign = Tools_Drawing::ALIGN_TOP;
|
||||
} elseif ($value->valign[1]) {
|
||||
$valign = Tools_Drawing::ALIGN_CENTER;
|
||||
} else {
|
||||
$valign = Drawing::ALIGN_BOTTOM;
|
||||
$valign = Tools_Drawing::ALIGN_BOTTOM;
|
||||
}
|
||||
|
||||
Drawing::imagettftextbox(
|
||||
$this->image,
|
||||
$size,
|
||||
0,
|
||||
$value->left,
|
||||
$value->top,
|
||||
$color,
|
||||
$fontfile,
|
||||
$text,
|
||||
$value->width,
|
||||
$value->height,
|
||||
$align,
|
||||
$valign
|
||||
);
|
||||
Tools_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));
|
||||
if($new_height == false) {
|
||||
$new_height = ceil($height * $new_width / $width);
|
||||
}
|
||||
|
||||
// Resample
|
||||
$image_p = imagecreatetruecolor($new_width, $new_height);
|
||||
imagecopyresampled($image_p, $this->image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
|
||||
// imagecopyresized($image_p, $this->image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
|
||||
// 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) {
|
||||
$this->_prepare = false;
|
||||
function prepare() {
|
||||
if($this->prepare) {
|
||||
$this->prepare = false;
|
||||
foreach ($this->data as $value) {
|
||||
$this->imageText($value->text, $value); // break;
|
||||
}
|
||||
|
|
@ -228,8 +184,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) {
|
||||
|
|
|
|||
27
src/UTF8.php
27
src/UTF8.php
|
|
@ -1,21 +1,12 @@
|
|||
<?php
|
||||
|
||||
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;
|
||||
|
||||
$matches = [];
|
||||
preg_match_all('/.{' . $split_length . '}|[^\x00]{1,' . $split_length . '}$/us', $str, $matches);
|
||||
|
||||
return $matches[0];
|
||||
class UTF8 {
|
||||
static function str_split($str, $split_length = 1) {
|
||||
$split_length = (int) $split_length;
|
||||
|
||||
$matches = array();
|
||||
preg_match_all('/.{'.$split_length.'}|[^\x00]{1,'.$split_length.'}$/us', $str, $matches);
|
||||
|
||||
return $matches[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Url.php
31
src/Url.php
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso;
|
||||
|
||||
class Url {
|
||||
/** @var array<string, string> */
|
||||
public array $parts = [];
|
||||
public ?Url $parent;
|
||||
|
||||
/**
|
||||
* @param Url|null $parent
|
||||
*/
|
||||
function setParent($parent): void {
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $parts
|
||||
*/
|
||||
function setQuery(array $parts): void {
|
||||
$this->parts = $parts;
|
||||
}
|
||||
|
||||
function addQueryParam(string $key, ?string $value): void {
|
||||
$this->parts[$key] = $value;
|
||||
}
|
||||
|
||||
function toString(): string {
|
||||
return '?' . http_build_query(array_merge($this->parts, $this->parent ? $this->parent->parts : []));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* UserMessageException.php
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Исключение с понятным пользователю сообщением, которое имеет смысл ему показать.
|
||||
* @see Controller_Front
|
||||
*/
|
||||
namespace ctiso;
|
||||
|
||||
class UserMessageException extends \Exception {
|
||||
/** @var string */
|
||||
class UserMessageException extends Exception {
|
||||
public $userMessage;
|
||||
/**
|
||||
* @param string $message
|
||||
*/
|
||||
public function __construct($message) {
|
||||
parent::__construct($message);
|
||||
$this->userMessage = $message;
|
||||
|
|
|
|||
45
src/Validator/Rule/Abstract.php
Normal file
45
src/Validator/Rule/Abstract.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
abstract class Validator_Rule_Abstract
|
||||
{
|
||||
public $field;
|
||||
protected $errorMsg;
|
||||
protected $ctx;
|
||||
|
||||
public function __construct($field, $errorMsg = null)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->errorMsg = $errorMsg;
|
||||
}
|
||||
|
||||
public function setName($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setErrorMsg($errorMsg)
|
||||
{
|
||||
$this->errorMsg = $errorMsg;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getErrorMsg()
|
||||
{
|
||||
return $this->errorMsg;
|
||||
}
|
||||
|
||||
public function isValid(Collection $container, $status = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
function skipEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setContext($ctx)
|
||||
{
|
||||
$this->ctx = $ctx;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Validator\Rule;
|
||||
|
||||
use ctiso\Collection;
|
||||
|
||||
abstract class AbstractRule
|
||||
{
|
||||
public string $field;
|
||||
protected ?string $errorMsg;
|
||||
/** @var RuleContext */
|
||||
protected $ctx;
|
||||
|
||||
public function __construct(string $field, ?string $errorMsg = null)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->errorMsg = $errorMsg;
|
||||
}
|
||||
|
||||
public function setName(string $field): self
|
||||
{
|
||||
$this->field = $field;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setErrorMsg(?string $errorMsg): self
|
||||
{
|
||||
$this->errorMsg = $errorMsg;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getErrorMsg(): string
|
||||
{
|
||||
return $this->errorMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $container
|
||||
* @param bool|null $status
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid(Collection $container, $status = null): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
function skipEmpty(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RuleContext $ctx
|
||||
*/
|
||||
public function setContext($ctx): void
|
||||
{
|
||||
$this->ctx = $ctx;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,19 +3,14 @@
|
|||
/**
|
||||
* Проверка на число
|
||||
*/
|
||||
namespace ctiso\Validator\Rule;
|
||||
|
||||
use ctiso\Validator\Rule\AbstractRule;
|
||||
use ctiso\Collection;
|
||||
|
||||
class Alpha extends AbstractRule
|
||||
class Validator_Rule_Alpha extends Validator_Rule_Abstract
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,14 @@
|
|||
/**
|
||||
* Проверка формата электронной почты
|
||||
*/
|
||||
namespace ctiso\Validator\Rule;
|
||||
use ctiso\Validator\Rule\AbstractRule,
|
||||
ctiso\Collection;
|
||||
|
||||
class Code extends AbstractRule
|
||||
class Validator_Rule_Code extends Validator_Rule_Abstract
|
||||
{
|
||||
public function getErrorMsg(): string {
|
||||
public function getErrorMsg()
|
||||
{
|
||||
return "Неправильно указан персональный код";
|
||||
}
|
||||
|
||||
function checkCode(array $code): bool {
|
||||
function checkCode($code) {
|
||||
foreach($code as $c) {
|
||||
if (empty($c)) {
|
||||
return false;
|
||||
|
|
@ -22,38 +19,37 @@ class Code extends AbstractRule
|
|||
return true;
|
||||
}
|
||||
|
||||
public function isValid(Collection $container, $status = null): bool
|
||||
public function isValid(Collection $container, $status = null)
|
||||
{
|
||||
if ($status == 'update') return true;
|
||||
$name = $this->field;
|
||||
|
||||
if (is_array($_POST[$name . '_code_genre'])) {
|
||||
$count = count($_POST[$name . '_code_genre']);
|
||||
for($n = 0; $n < $count; $n++) {
|
||||
$code = [
|
||||
$_POST[$name . '_code_genre'][$n],
|
||||
$_POST[$name . '_code_f'][$n],
|
||||
$_POST[$name . '_code_i'][$n],
|
||||
$_POST[$name . '_code_o'][$n],
|
||||
$_POST[$name . '_code_year'][$n],
|
||||
$_POST[$name . '_code_month'][$n],
|
||||
$_POST[$name . '_code_day'][$n]
|
||||
];
|
||||
for($n = 0; $n < count($_POST[$name . '_code_genre']); $n++) {
|
||||
$code = array(
|
||||
$_POST[$name . '_code_genre'][$n],
|
||||
$_POST[$name . '_code_f'][$n],
|
||||
$_POST[$name . '_code_i'][$n],
|
||||
$_POST[$name . '_code_o'][$n],
|
||||
$_POST[$name . '_code_year'][$n],
|
||||
$_POST[$name . '_code_month'][$n],
|
||||
$_POST[$name . '_code_day'][$n]
|
||||
);
|
||||
if (!$this->checkCode($code)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
$code = [
|
||||
$_POST[$name . '_code_genre'],
|
||||
$_POST[$name . '_code_f'],
|
||||
$_POST[$name . '_code_i'],
|
||||
$_POST[$name . '_code_o'],
|
||||
$_POST[$name . '_code_year'],
|
||||
$_POST[$name . '_code_month'],
|
||||
} else {
|
||||
$code = array(
|
||||
$_POST[$name . '_code_genre'],
|
||||
$_POST[$name . '_code_f'],
|
||||
$_POST[$name . '_code_i'],
|
||||
$_POST[$name . '_code_o'],
|
||||
$_POST[$name . '_code_year'],
|
||||
$_POST[$name . '_code_month'],
|
||||
$_POST[$name . '_code_day']
|
||||
];
|
||||
);
|
||||
|
||||
return $this->checkCode($code);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,38 +3,30 @@
|
|||
/**
|
||||
* Проверка формата даты
|
||||
*/
|
||||
namespace ctiso\Validator\Rule;
|
||||
use ctiso\Validator\Rule\AbstractRule,
|
||||
ctiso\Collection;
|
||||
|
||||
class Count extends AbstractRule
|
||||
class Validator_Rule_Count extends Validator_Rule_Abstract
|
||||
{
|
||||
public int $size = 1;
|
||||
public ?int $max = null;
|
||||
public $size = 1;
|
||||
public $max = false;
|
||||
|
||||
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))), array($this, 'not_empty')));
|
||||
|
||||
return $count >= $this->size && $count <= ((int)$this->max);
|
||||
return $count >= $this->size && $count <= $this->max;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,23 +3,21 @@
|
|||
/**
|
||||
* Проверка формата даты
|
||||
*/
|
||||
namespace ctiso\Validator\Rule;
|
||||
use ctiso\Validator\Rule\AbstractRule,
|
||||
ctiso\Collection;
|
||||
|
||||
class Date extends AbstractRule
|
||||
class Validator_Rule_Date extends Validator_Rule_Abstract
|
||||
{
|
||||
public function getErrorMsg(): string
|
||||
{
|
||||
private $split = "\\/";
|
||||
|
||||
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)
|
||||
&& checkdate((int)$matches[2], (int)$matches[1], (int)$matches[3]));
|
||||
return (preg_match($pattern, $container->get($this->field), $matches)
|
||||
&& checkdate($matches[2], $matches[1], $matches[3]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,21 +3,23 @@
|
|||
/**
|
||||
* Проверка формата электронной почты
|
||||
*/
|
||||
namespace ctiso\Validator\Rule;
|
||||
use ctiso\Validator\Rule\AbstractRule,
|
||||
ctiso\Collection;
|
||||
|
||||
class Email extends AbstractRule
|
||||
class Validator_Rule_Email extends Validator_Rule_Abstract
|
||||
{
|
||||
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 = explode(",", $container->get($this->field));
|
||||
foreach ($emails as $email) {
|
||||
// if (! preg_match("/^$user@($doIsValid|(\[($ipv4|$ipv6)\]))$/", $email)) return false;
|
||||
if (! filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,20 +3,22 @@
|
|||
/**
|
||||
* Проверка формата электронной почты
|
||||
*/
|
||||
namespace ctiso\Validator\Rule;
|
||||
use ctiso\Validator\Rule\AbstractRule,
|
||||
ctiso\Collection;
|
||||
|
||||
class EmailList extends AbstractRule
|
||||
class Validator_Rule_EmailList extends Validator_Rule_Abstract
|
||||
{
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace ctiso\Validator\Rule;
|
||||
use ctiso\Validator\Rule\AbstractRule,
|
||||
ctiso\Collection,
|
||||
ctiso\Path;
|
||||
class Validator_Rule_FileName extends Validator_Rule_Abstract {
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,48 +3,44 @@
|
|||
/**
|
||||
* Проверка формата времени
|
||||
*/
|
||||
namespace ctiso\Validator\Rule;
|
||||
use ctiso\Validator\Rule\AbstractRule,
|
||||
ctiso\Collection;
|
||||
|
||||
class IsFile extends AbstractRule
|
||||
class Validator_Rule_IsFile extends Validator_Rule_Abstract
|
||||
{
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -3,22 +3,16 @@
|
|||
/**
|
||||
* Проверка на равентство двух полей
|
||||
*/
|
||||
namespace ctiso\Validator\Rule;
|
||||
use ctiso\Validator\Rule\AbstractRule,
|
||||
ctiso\Collection;
|
||||
|
||||
class MatchRule extends AbstractRule
|
||||
class Validator_Rule_Match extends Validator_Rule_Abstract
|
||||
{
|
||||
/** @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);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue