diff --git a/src/Adapter.php b/src/Adapter.php index 2ba68f9..6ab0934 100644 --- a/src/Adapter.php +++ b/src/Adapter.php @@ -8,12 +8,21 @@ 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)) { diff --git a/src/Arr.php b/src/Arr.php index 38cbb35..2fa9187 100644 --- a/src/Arr.php +++ b/src/Arr.php @@ -4,6 +4,12 @@ namespace ctiso; class Arr { + /** + * @param array $data + * @param string|int $key + * @param mixed $default + * @return mixed + */ static function get($data, $key, $default = null) { return $data[$key] ?? $default; } diff --git a/src/Collection.php b/src/Collection.php index 2f7cea3..430cc69 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -3,21 +3,18 @@ namespace ctiso; /** * Коллекция - * + * @implements \ArrayAccess */ class Collection implements \ArrayAccess { - /** - * Holds collective request data - * - * @var array - */ - protected $data = array(); + /** @var array */ + protected $data = []; /** * Преобразование массива в коллекцию * * @param array $data + * @return bool */ public function import(array $data) { @@ -27,6 +24,8 @@ class Collection implements \ArrayAccess /** * Преобразование коллекции в массив + * + * @return array */ public function export() { @@ -34,14 +33,13 @@ class Collection implements \ArrayAccess } /** - * Store "request data" in GPC order. - * + * @param string $key * @param mixed $value * * @return void */ - public function set($key/*: string*/, $value/*: any*/) + public function set(string $key, mixed $value) { $this->data[$key] = $value; } @@ -50,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) @@ -58,23 +56,93 @@ class Collection implements \ArrayAccess return isset($this->data[$key]) && $this->data[$key] != '' ? $this->data[$key] : $default; } - public function getInt($key, $default = 0) + /** + * @param string $key + * @param int $default + * @return int + */ + public function getInt(string $key, int $default = 0): int { - return (int)$this->get($key, $default); + $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; } - public function getString($key, $default = '') + /** + * @param string $key + * @param string $default + * @return string + */ + public function getString(string $key, string $default = ''): string { - return (string)$this->get($key, $default); + $value = $this->get($key); + + if (is_string($value)) { + return $value; + } + + if (is_numeric($value)) { + return (string)$value; + } + + return $default; } - public function getNat($key, $default = 1) + /** + * Получает булево значение + * Поддерживает: 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 { $result = (int)$this->get($key, $default); return (($result > 0) ? $result : $default); } - public function clear() + public function clear(): void { $this->data = []; } diff --git a/src/ComponentRequest.php b/src/ComponentRequest.php index 6bc7dbc..378c375 100644 --- a/src/ComponentRequest.php +++ b/src/ComponentRequest.php @@ -2,19 +2,31 @@ namespace ctiso; -use ctiso\HttpRequest, - ctiso\Arr; +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); @@ -30,6 +42,9 @@ class ComponentRequest { return $default; } + /** + * @return string + */ function getAction() { return $this->r->getAction(); } diff --git a/src/Connection/HttpRequest.php b/src/Connection/HttpRequest.php index 006cc63..368feda 100644 --- a/src/Connection/HttpRequest.php +++ b/src/Connection/HttpRequest.php @@ -3,27 +3,37 @@ namespace ctiso\Connection; use ctiso\File; -class HttpRequest +class HttpRequest { const POST = "POST"; const GET = "GET"; - private $param = array(); // Параметры запроса - public $data = null; // Содержание - public $url; // Адресс - public $method; // Метод + /** @var array Параметры запроса */ + private $param = []; + /** @var string Содержание */ + public $data = null; + /** @var string Адресс */ + public $url; + /** @var string Метод */ + public $method; + /** @var int */ 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() { @@ -36,47 +46,58 @@ class HttpRequest $result .= $this->data; return $result; } - + /** * Установка параметров запроса - * @parma string $name - * @parma string $value + * @param string $name + * @param string $value */ - public function setParameter($name, $value) + public function setParameter($name, $value): void { $this->param[$name] = $value; } /** * Метод запроса GET или POST + * @param string $method */ - public function setMethod($method) + public function setMethod($method): void { $this->method = $method; } - public function setUrl($url) + /** + * Установка URL + * @param string $url + */ + public function setUrl($url): void { $this->url = $url; - $this->host = parse_url($this->url, PHP_URL_HOST); + $host = parse_url($this->url, PHP_URL_HOST); + if (!$host) { + throw new \RuntimeException("Не удалось получить хост"); + } + $this->host = $host; } - public function getUrl() + public function getUrl(): string { return $this->url; } /** * Содержание запроса + * @param string $data */ - public function setContent($data) + public function setContent($data): void { - $this->setParameter ("Content-length", strlen($data)); + $this->setParameter("Content-length", (string)strlen($data)); $this->data = $data; } /** * Посылает запрос и возвращает страницу + * @return string|null */ public function getPage() { @@ -89,7 +110,7 @@ class HttpRequest $header = $this->getHeader(); fwrite($socket, $header); - $result = null; + $result = ''; while (! feof($socket)) { $result .= fgets($socket, 128); } @@ -99,6 +120,12 @@ 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; diff --git a/src/Connection/HttpResponse.php b/src/Connection/HttpResponse.php index 1da2036..fad35ab 100644 --- a/src/Connection/HttpResponse.php +++ b/src/Connection/HttpResponse.php @@ -7,30 +7,39 @@ namespace ctiso\Connection; class HttpResponse { + /** @var int */ private $offset; - private $param = array (); + /** @var array */ + private $param = []; + /** @var int */ 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() + private function parseMessage(): void { $http = explode(" ", $this->getLine()); $this->version = $http[0]; - $this->code = $http[1]; + $this->code = (int)$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(); @@ -38,12 +47,12 @@ class HttpResponse if (isset($this->param['Transfer-Encoding']) && $this->param['Transfer-Encoding'] == 'chunked') { //$this->data = substr($this->response, $this->offset); - $index = hexdec($this->getLine()); + $index = (int)hexdec($this->getLine()); $chunk = []; while ($index > 0) { $chunk [] = substr($this->response, $this->offset, $index); $this->offset += $index; - $index = hexdec($this->getLine()); + $index = (int)hexdec($this->getLine()); } $this->data = implode("", $chunk); @@ -55,32 +64,33 @@ class HttpResponse /** * Обработка строки HTTP ответа */ - private function getLine() + private function getLine(): string { $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) + public function getParameter($name): string { return $this->param[$name]; } - public function getData() + public function getData(): string { return $this->data; } /** - * Состояние + * Состояние */ - public function getCode() + public function getCode(): int { return $this->code; } diff --git a/src/Controller/Action.php b/src/Controller/Action.php index 4393ab7..84cf844 100644 --- a/src/Controller/Action.php +++ b/src/Controller/Action.php @@ -1,63 +1,69 @@ part = new Url(); } - public function setUp() { + public function setUp(): void { } + /** + * Загрузка файла настроек + * @param string $name + * @return array + */ public function loadConfig($name) { $basePath = $this->config->get('site', 'path'); @@ -71,25 +77,40 @@ class Action return $settings; } - public function getConnection() + public function getConnection(): Database { return $this->db; } + /** + * Путь к установке модуля + * @param string $name + * @return string + */ public function installPath($name) { $basePath = $this->config->get('system', 'path'); return Path::join($basePath, "modules", $name); } - public function addSuggest(View $view, $name) { - $suggest = []; + /** + * Добавляет подсказки + * @param View $view + * @param string $name + */ + public function addSuggest(View $view, $name): void { $file = Path::join($this->modulePath, 'help', $name . '.suggest'); if (file_exists($file)) { $view->suggestions = include($file); } } + /** + * Поиск иконки + * @param string $icon + * @param int $size + * @return string Путь к иконке + */ function findIcon($icon, $size) { $webPath = $this->config->get('site', 'web'); return Path::join($webPath, 'icons', $size . 'x' . $size, $icon . '.png'); @@ -109,7 +130,8 @@ class Action $webPath = $this->config->get('system', 'web'); $list = [ - Path::join($this->modulePath, 'templates', $this->viewPathPrefix) => Path::join($webPath, "modules", $this->name, 'templates', $this->viewPathPrefix), + Path::join($this->modulePath, 'templates', $this->viewPathPrefix) + => Path::join($webPath, "modules", $this->name, 'templates', $this->viewPathPrefix), Path::join($basePath, "templates") => Path::join($webPath, "templates") ]; @@ -164,6 +186,7 @@ class Action * 1. Можно переопределить действия * 2. Использовать наследование чтобы добавить к старому обработчику новое поведение * @param HttpRequest $request запроса + * @return View|string */ public function preProcess(HttpRequest $request) { @@ -179,29 +202,47 @@ class Action return $view; } + /** + * Выполнение действия + * @param HttpRequest $request + * @return View|string|false + */ public function execute(HttpRequest $request) { $result = $this->preProcess($request); - if (!empty($result)) { - $this->view = $result; - } - $text = $this->render(); - return $text; + return $result; } + /** + * Перенаправление на другой контроллер + * @param string $action + * @param HttpRequest $args + * @return mixed + */ public function forward($action, HttpRequest $args) { - $value = call_user_func([$this, $action], $args); + $actionFn = [$this, $action]; + if (!is_callable($actionFn)) { + return false; + } + $value = call_user_func($actionFn, $args); return $value; } /** * Страница по умолчанию + * @param HttpRequest $request + * @return string|false */ public function actionIndex(HttpRequest $request) { return ""; } - public function addUrlPart($key, $value) { + /** + * Добавление части ссылки + * @param string $key + * @param string $value + */ + public function addUrlPart($key, $value): void { $this->part->addQueryParam($key, $value); } @@ -214,10 +255,9 @@ class Action */ public function nUrl($actionName, array $param = []) { - $access/*: ActionAccess*/ = $this->access; + $access = $this->access; $url = new Url(); - //print_r([$name, $param]); if ($access == null || $access->checkAction($actionName)) { $moduleName = explode("\\", strtolower(get_class($this))); if (count($moduleName) > 2) { @@ -238,7 +278,7 @@ class Action /** * Генерация ссылки на действие контроллера * Ajax определяется автоматически mode = ajax используется для смены layout - * @param $name + * @param string $name * @param array $param * @return Url|null * @@ -250,31 +290,13 @@ class Action return $this->nUrl($name, array_merge(['mode' => 'ajax'], $param)); } - /** - * Добавление помошника контроллера - */ - public function addHelper($class) - { - $this->helpers [] = $class; - } - - /** - * Вызов помошников контроллера - */ - public function callHelpers(HttpRequest $request) - { - $action = self::ACTION_PREFIX . $request->getAction(); - foreach ($this->helpers as $helper) { - if (method_exists($helper, $action)) { - return call_user_func([$helper, $action], $request, $this); - } else { - return $helper->actionIndex($request, $this); // Вместо return response ??? - } - } - } - /** * Загрузка файла класса + * @deprecated Веместо его нужно использовать автозагрузку + * @param string $path + * @param mixed $setup + * @param string $prefix + * @return mixed */ public function loadClass($path, $setup = null, $prefix = '') { @@ -286,6 +308,11 @@ class Action throw new Exception("NO CLASS $path"); } + /** + * Загрузка настроек + * @param string $path + * @return array + */ public function loadSettings($path) { $result = new Settings($path); @@ -293,69 +320,21 @@ class Action return $result->export(); } - public function setView($name) - { - $this->view = $this->getView($name); - } - /** - * Установка заголовка для отображения + * Установка идентификатора страницы + * @return int */ - public function setTitle($title) - { - $this->view->setTitle($title); - } - - /** - * Добавление widget к отображению - */ - public function addChild($section, $node) - { - $this->childNodes[$section] = $node; - } - - public function setValue($name, $value) - { - $this->ctrlValues[$name] = $value; - } - - /** - * Добавление дочернего отображения к текущему отображению - */ - public function addView($section, $node) - { - $this->childViews[$section] = $node; - } - - /** - * Генерация содержания - * Путаница c execute и render - */ - public function render() - { - $view = $this->view; - if ($view instanceof View) { - $this->view->assignValues($this->ctrlValues); - - $node/*: Composite*/ = null; - foreach ($this->childNodes as $name => $node) { - $node->make($this); - $this->view->setView($name, $node->view); - } - - foreach ($this->childViews as $name => $node) { - $this->view->setView($name, $node); - } - } - return $this->view; - } - function getPageId(HttpRequest $request) { $pageId = time(); $request->session()->set('page', $pageId); return $pageId; } + /** + * Проверка идентификатора страницы + * @param int $page Идентификатор страницы + * @return bool + */ function checkPageId(HttpRequest $request, $page) { if ($request->get('__forced__')) { @@ -367,16 +346,14 @@ class Action return $result; } + /** + * @return State + */ function _getActionPath() { return new State('index'); } - // Тоже убрать в метод Controller_Model - function getActionPath(HttpRequest $request, $action = null) { - $this->_getActionPath()->getPath($this, ($action) ? $action : $request->getAction()); - } - - function redirect($action/*: string*/) { + function redirect(string $action): void { header('location: ' . $action); exit(); } diff --git a/src/Controller/ActionInterface.php b/src/Controller/ActionInterface.php new file mode 100644 index 0000000..4c8dbf6 --- /dev/null +++ b/src/Controller/ActionInterface.php @@ -0,0 +1,28 @@ + $class + * @return \ctiso\View\View + */ + function getView($name, $class); + /** + * @param string $key + * @param string $value + */ + function addUrlPart($key, $value): void; +} \ No newline at end of file diff --git a/src/Controller/Component.php b/src/Controller/Component.php index 993521b..124d4e5 100644 --- a/src/Controller/Component.php +++ b/src/Controller/Component.php @@ -13,49 +13,42 @@ use ctiso\Database; use ctiso\Collection; use ctiso\Registry; use ctiso\Controller\SiteInterface; +use ctiso\Database\PDOStatement; use PHPTAL; use PHPTAL_PreFilter_Normalize; - -class FakeTemplate { - public $_data = []; - public $_name = ''; - - function __construct($name) { - $this->_name = $name; - } - - function __set($key, $value) { - $this->_data[$key] = $value; - } - - function execute() { - return json_encode($this->_data); - } -} +use ctiso\View\JsonView; /** * Класс компонента */ class Component { + /** @var string[] */ public $viewPath = []; + /** @var string[] */ public $webPath = []; + /** @var ?string */ public $template = null; public string $templatePath; + /** @var int */ public $component_id; + /** @var string */ public $component_title; - + /** @var string */ public $COMPONENTS_WEB; public Registry $config; public Database $db; public Collection $parameter; + /** @var string */ public $output = 'html'; + /** @var string */ public $module; + /** @var string */ public $item_module; /** @@ -63,17 +56,35 @@ class Component */ public $site; - function before() { + function before(): void { } + /** + * @param string $match + * @return string + */ static function replaceContent($match) { return \ctiso\Tales::phptal_component(htmlspecialchars_decode($match[3])); } + /** + * @param string $text + * @return string + */ static function applyComponents($text) { - return preg_replace_callback('/<(\w+)(\s+[a-zA-Z\-]+=\"[^\"]*\")*\s+tal:replace="structure\s+component:([^\"]*)"[^>]*>/u', 'ctiso\\Controller\\Component::replaceContent', $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); @@ -85,29 +96,36 @@ class Component } $this->before(); - if (method_exists($this, $action)) { - return call_user_func([$this, $action], $crequest); - } else { - return $this->actionIndex($crequest); + $actionMethod = [$this, $action]; + if (is_callable($actionMethod)) { + return call_user_func($actionMethod, $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'); } + /** + * Получить шаблон + * @param string $name + * @return PHPTAL|JsonView + */ public function getView($name) { if ($this->output === 'json') { - return new FakeTemplate($name); + return new JsonView($name); } - /* @var Registry $config */ + /** @var Registry $config */ $config = $this->config; $default = $config->get('site', 'template'); $template = ($this->template) ? $this->template : $this->getTemplateName($config); @@ -117,7 +135,7 @@ class Component foreach ($this->viewPath as $index => $viewPath) { // Загружать шаблон по умолчанию если не найден текущий $dir = Path::join($this->viewPath[$index], 'templates', $template); - if(is_dir($dir)) { + if (is_dir($dir)) { $tpl = new PHPTAL(Path::join($this->viewPath[$index], 'templates', $template, $name)); $tpl->setPhpCodeDestination(PHPTAL_PHP_CODE_DESTINATION); $selected = $index; @@ -152,10 +170,19 @@ 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; // Брать настройки из куков если есть @@ -169,6 +196,10 @@ class Component return Path::join($this->viewPath[count($this->viewPath) - 1], 'templates', 'modern', $name); } + /** + * Возвращает путь к шаблонам + * @return string + */ public function getTemplateWebPath() { return Path::join($this->webPath[count($this->webPath) - 1], 'templates', 'modern'); @@ -176,12 +207,13 @@ class Component /** * Создает модель - * @param string $name - * @return mixed + * + * @template T + * @param class-string $modelName + * @return T */ - public function getModel($name) + public function getModel($modelName) { - $modelName = "App\\Mapper\\" . $name; $model = new $modelName(); $model->config = $this->config; $model->db = $this->db; @@ -191,7 +223,8 @@ class Component /** * @param string $key * @param string $val - * @param $res + * @param PDOStatement $res + * @return array{value: string, name: string}[] */ public function options(string $key, string $val, $res) { $result = []; @@ -201,6 +234,11 @@ class Component return $result; } + /** + * @param array $list + * @param bool $selected + * @return array{value: string, name: string, selected: bool}[] + */ public function optionsPair(array $list, $selected = false) { $result = []; foreach ($list as $key => $value) { @@ -225,6 +263,10 @@ class Component return null; } + /** + * Получить информацию о параметрах + * @return array + */ function getInfo() { $filename = Path::join($this->viewPath[count($this->viewPath) - 1], 'install.json'); if (file_exists($filename)) { @@ -239,9 +281,9 @@ class Component /** * Генерация интерфейса для выбора галлереи фотографии * @param Composite $view - * @param \ctiso\Form\OptionsFactory $options + * @param ?\ctiso\Form\OptionsFactory $options */ - public function setParameters(Composite $view, $options = null) + public function setParameters(Composite $view, $options = null): void { $form = new Form(); @@ -253,9 +295,14 @@ class Component $view->component_title = $settings['title']; } + /** + * @param \ctiso\Form\OptionsFactory $options + * @return array + */ public function getFields($options = null) { $form = new Form(); - $form->addFieldList($this->getInfo()['parameter'], $options); + $settings = $this->getInfo(); + $form->addFieldList($settings['parameter'], $options); return $form->field; } @@ -282,16 +329,19 @@ class Component $name = $path; $config = $site->getConfig(); + // FIXME: Если имя для компонента не задано то возвращаем пустой компонент + // Нужно дополнительно проверить и файл или в autoloader просто не найдет файл копонента + if (!$name) { + return new Component(); + } + $filename = ucfirst($name); $path = Path::join ($config->get('site', 'components'), $name, $filename . '.php'); $className = implode("\\", ['Components', ucfirst($name), $filename]); - /** - * @var ?Component $component - */ $component = null; - if (file_exists($path)) { + /** @var Component $component */ $component = new $className(); $component->viewPath = [$config->get('site', 'components') . '/' . $name . '/']; @@ -299,6 +349,7 @@ class Component $component->COMPONENTS_WEB = $config->get('site', 'web') . '/components/'; } else { + /** @var Component $component */ $component = new $className(); $template = $component->getTemplateName($site->getConfig()); @@ -371,12 +422,16 @@ class Component return $component; } + /** + * @return ?array{name: string, url: string} + */ function getEditUrl() { return null; } /** * @param ComponentRequest $request + * @return array */ function raw_query($request) { @@ -403,6 +458,8 @@ class Component /** * @param ComponentRequest $request + * @param array $list + * @return string */ function query($request, $list) { @@ -416,13 +473,28 @@ class Component return '?' . http_build_query($arr); } - function addRequireJsPath($name, $path, $shim = null) { + /** + * @param string $name + * @param string $path + * @param array $shim + */ + function addRequireJsPath($name, $path, $shim = null): void { $this->site->addRequireJsPath($name, $path, $shim); } /** * @param ComponentRequest $request + * @return mixed */ function actionIndex($request) { + return ""; + } + + /** + * @param HttpRequest $request + * @return array + */ + function getDefaultPageEnvironment($request) { + return []; } } diff --git a/src/Controller/Front.php b/src/Controller/Front.php index d1bc119..b799f40 100644 --- a/src/Controller/Front.php +++ b/src/Controller/Front.php @@ -5,25 +5,37 @@ * @package system.controller */ namespace ctiso\Controller; -use ctiso\Controller\Action, - ctiso\Registry, - ctiso\Database, - ctiso\Collection, - ctiso\Filter\ActionAccess, - ctiso\Filter\ActionLogger, - ctiso\Path, - ctiso\UserMessageException, - ctiso\HttpRequest, - ctiso\Role\User; + +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 { - protected $_param; // Параметр по которому выбирается модуль - protected $default; // Значение параметра по умолчанию + /** + * Параметр по которому выбирается модуль + * @var string + */ + protected $_param; + /** + * Значение параметра по умолчанию + * @var string + */ + protected $default; - protected $modules = array(); + /** @var array */ + protected $modules = []; + /** + * @param string $default + */ public function __construct(Database $db, Registry $config, User $user, $default) { parent::__construct(); $this->config = $config; @@ -32,7 +44,13 @@ class Front extends Action $this->default = $default; } - public function isLoaded($name) { + /** + * Проверяет загружен ли модуль + * @param string $name Имя модуля + * @return bool + */ + public function isLoaded($name): bool + { return isset($this->modules[$name]); } @@ -63,6 +81,7 @@ class Front extends Action $ucpart = ucfirst($second); $moduleClass = "Modules\\$ucname\\$ucpart"; + /** @var Action $module */ $module = new $moduleClass(); // Инициализация модуля @@ -92,7 +111,7 @@ class Front extends Action public function execute(HttpRequest $request) { - $name = $request->get('module', $this->default); + $name = $request->getString('module', $this->default); try { return $this->loadModule($name, $request); } catch (UserMessageException $ex) { //Исключение с понятным пользователю сообщением diff --git a/src/Controller/Installer.php b/src/Controller/Installer.php index e00b705..79b08f2 100644 --- a/src/Controller/Installer.php +++ b/src/Controller/Installer.php @@ -1,38 +1,64 @@ _registry = $_registry; } - - public function setUp($db_manager, $installPath) + + /** + * Устанавливает параметры + * @param Manager $db_manager + * @param callable $installPath + */ + public function setUp($db_manager, $installPath): void { $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; } - function getUninstallFile($name) { + /** + * Получение пути к файлу unisntall.json + * @param string $name + * @return string + */ + 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); @@ -46,6 +72,14 @@ 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 = []; @@ -60,18 +94,28 @@ class Installer return $result; } - function uninstall($name){ + /** + * @param string $name + * @return void + */ + function uninstall($name): void + { $uninstall = $this->getUninstallFile($name); if (file_exists($uninstall)) { $json_installer = new JsonInstall($this->db_manager); - $json_installer->install($uninstall,null); + $json_installer->install($uninstall, null); } $this->_registry->removeKey($name); $this->_registry->write(); } - // Устанавливает обновления если есть - function doUpdates($name, $force = false) // Установка модуля + /** + * Устанавливает обновления если есть + * @param string $name + * @param bool $force + * @return array + */ + function doUpdates($name, $force = false) { $result = []; $setup = $this->getSetupFile($name); @@ -86,7 +130,7 @@ class Installer $version_new = $settings->get('version'); if ($item) { - $version_old = $item['version']; + $version_old = $item['version']; } else { $version_old = "0.0"; $registry->writeKey([$name], []); @@ -96,15 +140,15 @@ class Installer if (is_array($sql)) { $res = $this->installSQL($sql, $version_new, $version_old, $name); if ($res) { - $result[]=$res; + $result[] = $res; } } - } + } // Обновление версии меню $registry->removeKey($name); $registry->set($name, [ - 'version' => $version_new, + 'version' => $version_new, 'time' => filemtime($setup) ]); // $registry->writeKey([$name], $settings->export()); @@ -114,7 +158,13 @@ class Installer return $result; } - function install($dbinit_path, $dbfill_path = null) { + /** + * Устанавливает базу данных + * @param string $dbinit_path + * @param string|null $dbfill_path + */ + function install($dbinit_path, $dbfill_path = null): void + { $json_installer = new JsonInstall($this->db_manager); $json_installer->install($dbinit_path, $dbfill_path); } diff --git a/src/Controller/Request.php b/src/Controller/Request.php index fb2d950..4a4293e 100644 --- a/src/Controller/Request.php +++ b/src/Controller/Request.php @@ -4,14 +4,25 @@ namespace ctiso\Controller; use ctiso\HttpRequest; class Request { + /** @var HttpRequest */ public $r; + /** @var string */ public $id; - function __construct($request/*: HttpRequest*/, $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; diff --git a/src/Controller/Service.php b/src/Controller/Service.php index 331475d..e4d7fb9 100644 --- a/src/Controller/Service.php +++ b/src/Controller/Service.php @@ -4,56 +4,70 @@ * Класс сервиса = Упрощенный компонент */ namespace ctiso\Controller; -use ctiso\Path, - ctiso\Model\BaseMapper, - ctiso\File, - ctiso\Registry, - ctiso\Database\PDOStatement; + +use ctiso\Path; +use ctiso\Model\BaseMapper; +use ctiso\File; +use ctiso\Registry; +use ctiso\Database\PDOStatement; +use ctiso\Database; class Service { + /** @var array */ public $viewPath = []; + /** @var array */ public $webPath = []; - public $config/*: Registry*/; + /** @var Registry */ + public $config; + /** @var string */ public $template; + /** @var string */ public $templatePath; + /** @var string */ public $COMPONENTS_WEB; - + /** @var Database */ public $db; + /** + * Возвращает путь к шаблонам + * @param string $name Имя шаблона + * @return string + */ public function getTemplatePath($name) { return Path::join($this->viewPath[0], 'templates', 'modern', $name); } + /** + * Возвращает путь к шаблонам + * @return string + */ public function getTemplateWebPath() { return Path::join($this->webPath[0], strtolower(get_class($this)), 'templates', 'modern'); } - /** - * @param string $name Имя модели - */ - private function getModelPath($name) - { - return Path::join ($this->config->get('system', 'path'), "model", $name . ".php"); - } - /** * Создает модель - * @param string $name + * @param class-string $modelName * @return BaseMapper */ - public function getModel($name) + public function getModel($modelName) { - require_once ($this->getModelPath ($name)); - $modelName = $name . "Mapper"; - $model = new $modelName (); + /** @var BaseMapper */ + $model = new $modelName(); $model->db = $this->db; return $model; } - public function options($key, $val, $res/*: PDOStatement*/) { + /** + * @param string $key + * @param string $val + * @param PDOStatement $res + * @return array + */ + public function options($key, $val, $res) { $result = []; while($res->next()) { $result[] = ['value' => $res->getInt($key), 'name' => $res->getString($val)]; @@ -61,6 +75,11 @@ class Service return $result; } + /** + * @param array $list + * @param bool $selected + * @return array + */ public function optionsPair($list, $selected = false) { $result = []; foreach ($list as $key => $value) { @@ -68,7 +87,10 @@ class Service } return $result; } - + + /** + * @return array + */ function getInfo() { $filename = Path::join($this->viewPath[0], 'install.json'); if (file_exists($filename)) { diff --git a/src/Controller/SiteInterface.php b/src/Controller/SiteInterface.php index e5abe67..92c06a5 100644 --- a/src/Controller/SiteInterface.php +++ b/src/Controller/SiteInterface.php @@ -3,15 +3,39 @@ 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(); - function addComponentConfig($config); - function addRequireJsPath(string $name, string $path, ?array $shim = null); - function addStyleSheet(string $url); + /** + * @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); + function replaceImg(string $src, int $width, int $height): string; + function updatePageTime(int $time): void; } diff --git a/src/Database.php b/src/Database.php index c967d6d..ab2a233 100644 --- a/src/Database.php +++ b/src/Database.php @@ -1,6 +1,10 @@ setAttribute(PDO::ATTR_STATEMENT_CLASS, [PDOStatement::class, []]); } - function prepare(string $sql, array $options = []): PDOStatement|false + /** + * prepare возвращает только PDOStatement т.к установлен PDO::ERRMODE_EXCEPTION + */ + function prepare(string $sql, array $options = []): PDOStatement { - $result/*: PDOStatement*/ = parent::prepare($sql, $options); + /** @var PDOStatement */ + $result = parent::prepare($sql, $options); return $result; } + function query($query, $fetchMode = PDO::FETCH_INTO, mixed $_arg1 = null, mixed $_arg2 = null): PDOStatement { + /** @var PDOStatement */ + $result = parent::query($query, $fetchMode); + return $result; + } + + /** + * Возвращает DSN + * @return DSN + */ public function getDSN() { return $this->dsn; } + + /** + * Возвращает true, если база данных Postgres + * @return bool + */ public function isPostgres() { return ($this->dsn["phptype"] == "pgsql"); } /** * Создает соединение с базой данных + * @param array $dsn - DSN + * @return Database|null */ static function getConnection(array $dsn) { + /** @var ?Database */ $connection = null; if ($dsn['phptype'] == 'pgsql' || $dsn['phptype'] == 'mysql') { $port = (isset($dsn['port'])) ? "port={$dsn['port']};" : ""; - $connection/*: Database*/ = new self("{$dsn['phptype']}:host={$dsn['hostspec']}; $port dbname={$dsn['database']}", $dsn['username'], $dsn['password']); + $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"'); } @@ -64,9 +100,9 @@ namespace ctiso { $connection = new self("{$dsn['phptype']}:"); $connection->sqliteCreateFunction('LOWER', 'sqliteLower', 1); } elseif ($dsn['phptype'] == 'sqlite') { - $connection/*: Database*/ = new self("{$dsn['phptype']}:{$dsn['database']}"); + $connection = new self("{$dsn['phptype']}:{$dsn['database']}"); $connection->setAttribute(PDO::ATTR_TIMEOUT, 5); - $mode = defined('SQLITE_JOURNAL_MODE') ? SQLITE_JOURNAL_MODE : 'WAL'; + $mode = defined('SQLITE_JOURNAL_MODE') ? \SQLITE_JOURNAL_MODE : 'WAL'; $connection->query("PRAGMA journal_mode=$mode"); $connection->sqliteCreateFunction('LOWER', 'sqliteLower', 1); } @@ -74,7 +110,12 @@ namespace ctiso { return $connection; } - public function executeQuery($query, $values = null): PDOStatement|bool + /** + * Выполняет запрос к базе данных + * @param string $query - запрос + * @param ?array $values - значения + */ + public function executeQuery($query, $values = null): PDOStatement { $stmt = $this->prepare($query); @@ -83,14 +124,21 @@ namespace ctiso { return $stmt; } + /** + * Создает подготовленный запрос + * @param string $query - запрос + * @return Statement + */ public function prepareStatement($query) { return new Statement($query, $this); } - // Для совместимости со старым представлением баз данных CIS /** - * Извлекает из базы все элементы по запросу + * Извлекает из базы все элементы по запросу (Для совместимости со старым представлением баз данных CIS) + * @param string $query - запрос + * @param ?array $values - значения + * @return array> */ public function fetchAllArray($query, $values = null) { @@ -102,6 +150,9 @@ namespace ctiso { /** * Извлекает из базы первый элемент по запросу + * @param string $query - запрос + * @param ?array $values - значения + * @return array|false */ public function fetchOneArray($query, $values = null) { @@ -111,6 +162,11 @@ namespace ctiso { return $sth->fetch(PDO::FETCH_ASSOC); } + /** + * Преобразует значения в подготовленные значения + * @param array $values - значения + * @return ?array + */ private function prepareValues($values) { if (!$values) { @@ -133,8 +189,14 @@ namespace ctiso { } return $prep; } + /** * Создает INSERT запрос + * @param string $table - таблица + * @param array $values - значения + * @param bool $return_id - возвращать id + * @param string $index - индекс + * @return int|null */ function insertQuery($table, array $values, $return_id = false, $index = null) { @@ -156,15 +218,22 @@ namespace ctiso { return $result[$index]; } else { $result = $this->fetchOneArray("SELECT $index AS lastid FROM $table WHERE OID = last_insert_rowid()"); + if ($result === false) { + throw new \RuntimeException("Ошибка получения идентификатора"); + } return $result['lastid']; } } + return null; } /** * Создает UPDATE запрос + * @param string $table - таблица + * @param array $values - значения + * @param string $cond - условие */ - function updateQuery($table, array $values, $cond) + function updateQuery($table, array $values, $cond): void { $prep = $this->prepareValues($values); $sql = "UPDATE $table SET " . implode( @@ -178,6 +247,10 @@ namespace ctiso { $stmt->execute($prep); } + /** + * Создает генератор идентификаторов + * @return IdGenerator + */ function getIdGenerator() { return new IdGenerator($this); @@ -191,12 +264,17 @@ namespace ctiso { function getNextId($seq) { $result = $this->fetchOneArray("SELECT nextval('$seq')"); + if ($result === false) { + throw new \RuntimeException("Ошибка получения следующего идентификатора"); + } return $result['nextval']; } - function close() + /** + * Закрывает соединение с базой данных + */ + function close(): void { - return null; } } } diff --git a/src/Database/IdGenerator.php b/src/Database/IdGenerator.php index 5ae4664..6cad31a 100644 --- a/src/Database/IdGenerator.php +++ b/src/Database/IdGenerator.php @@ -4,26 +4,40 @@ namespace ctiso\Database; use ctiso\Database; class IdGenerator { + /** @var Database */ private $db; function __construct(Database $db) { $this->db = $db; } + /** + * @return bool + */ function isBeforeInsert() { - return false; - } - - function isAfterInsert() { - return true; + return false; } - + + /** + * @return bool + */ + function isAfterInsert() { + 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']; } } diff --git a/src/Database/JsonInstall.php b/src/Database/JsonInstall.php index 3e6eef1..f7842fb 100644 --- a/src/Database/JsonInstall.php +++ b/src/Database/JsonInstall.php @@ -5,13 +5,21 @@ namespace ctiso\Database; use ctiso\Database\Manager; class JsonInstall { + /** @var Manager */ public $db_manager; + /** @var array */ public $serialColumns; public function __construct(Manager $db_manager) { $this->db_manager = $db_manager; } + /** + * Установить базу данных + * @param string $dbinit_path + * @param ?string $dbfill_path + * @return int + */ function install($dbinit_path, $dbfill_path = null) { $dbinit_file = file_get_contents($dbinit_path); if (is_string($dbinit_file)) { @@ -30,8 +38,14 @@ class JsonInstall { $this->fillDataBase($dbfill_path); } $this->makeConstraints($initActions); + return 1; } + /** + * Получить список таблиц, которые не существуют в базе данных + * @param array $tables + * @return array + */ function missingTables($tables) { $actual_tables = $this->db_manager->getAllTableNames(); $missingTables = []; @@ -42,8 +56,13 @@ class JsonInstall { return $missingTables; } - //Создать таблицы - function initDataBase($initActions/*: array*/, $dbinit_path) { + /** + * Создать таблицы + * @param array $initActions + * @param string $dbinit_path + * @return void + */ + function initDataBase(array $initActions, $dbinit_path) { $pg = $this->db_manager->db->isPostgres(); if (!$pg) { $refs = []; @@ -73,7 +92,7 @@ class JsonInstall { } } if ($action["type"] != "alterReference") { - $this->db_manager->ExecuteAction($action, $dbinit_path); + $this->db_manager->executeAction($action, $dbinit_path); } } @@ -95,7 +114,11 @@ class JsonInstall { } } - //Заполнить данными + /** + * Заполнить данными + * @param string $dbfill_file_path + * @return void + */ function fillDataBase($dbfill_file_path) { $dbfill_file = file_get_contents($dbfill_file_path); if (is_string($dbfill_file)) { @@ -128,7 +151,11 @@ class JsonInstall { } } - //Обновить ключи serial и создать ограничения + /** + * Обновить ключи serial и создать ограничения + * @param array $initActions + * @return void + */ function makeConstraints($initActions) { $pg = $this->db_manager->db->isPostgres(); if ($pg) { diff --git a/src/Database/Manager.php b/src/Database/Manager.php index 1629a92..ff8211e 100644 --- a/src/Database/Manager.php +++ b/src/Database/Manager.php @@ -7,18 +7,95 @@ 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, type: string}|string, + * fields:array, + * } + * + * @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 + * } + * + * @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 { - public $db/*: Database*/; + /** @var Database */ + public $db; public function __construct(Database $db) { $this->db = $db; } - public function executeAction($action/*: array*/, $db_file = "") + /** + * Выполняет действие + * @param Action $action + * @param string $db_file + * @throws Exception + */ + public function executeAction(array $action, $db_file = ""): void { - switch($action["type"]) { + switch ($action["type"]) { case "dropTable": $this->dropTableQuery($action["table_name"], true); break; @@ -55,59 +132,85 @@ class Manager break; default: - throw new Exception("unknown action ". $action["type"] . PHP_EOL); + throw new Exception("unknown action " . $action["type"] . PHP_EOL); } } - //Дропает и создаёт SQL VIEW - public function recreateView($viewName, $selectStatement) + /** + * Дропает и создаёт 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); + $this->db->query("DROP VIEW " . $viewName); + $this->db->query("CREATE VIEW " . $viewName . " AS " . $selectStatement); } - public function dropTableQuery($table, $cascade=false) + /** + * Дропает таблицу + * @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 = "DROP TABLE IF EXISTS " . $table; + if ($this->db->isPostgres() && $cascade) { $statement .= " CASCADE"; } $this->db->query($statement); } - public function alterReference($table, $column, $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.")"); + $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.");"); + $results = $this->db->fetchAllArray("PRAGMA table_info(" . $table . ");"); if (empty($results)) { return null; } $fields = []; foreach ($results as $result) { $fields[$result["name"]] = [ - "type"=> $result["type"], - "not_null"=> boolval($result["notnull"]), - "constraint"=> ((bool) $result["pk"]) ? "PRIMARY KEY" : null + "type" => $result["type"], + "not_null" => boolval($result["notnull"]), + "constraint" => ((bool) $result["pk"]) ? "PRIMARY KEY" : null ]; } return $fields; } } - public function renameColumn($table, $old_name, $new_name) + /** + * Переименование столбца в таблице + * @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); + $this->db->query("ALTER TABLE " . $table . " RENAME COLUMN " . $old_name . " TO " . $new_name); } else { $tmp_table = "tmp_" . $table; $this->dropTableQuery($tmp_table); @@ -117,9 +220,9 @@ class Manager return; } - $data/*: array*/ = $this->dumpTable($table); + $data = $this->dumpTable($table); - $this->db->query("ALTER TABLE ".$table." RENAME TO ".$tmp_table.";"); + $this->db->query("ALTER TABLE " . $table . " RENAME TO " . $tmp_table . ";"); $table_info[$new_name] = $table_info[$old_name]; unset($table_info[$old_name]); $this->createTableQuery($table, $table_info, null); @@ -134,42 +237,62 @@ class Manager } } - //Обновление ключа serial после ручной вставки - public function updateSerial($table, $column) + /** + * Обновление ключа 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); + $this->db->query("SELECT setval(pg_get_serial_sequence('" . $table . "', '" . $column . "'), coalesce(max(" . $column . "),0) + 1, false) FROM " . $table); } + /** + * Возвращает определение столбца + * @param string $name + * @param ColumnProps $data + * @param bool $pg + * @return string + */ public function columnDefinition($name, $data, $pg) { - $constraint = isset($data['constraint']) ? " ".$data['constraint'] : ""; + $constraint = isset($data['constraint']) ? " " . $data['constraint'] : ""; $references = ""; if (isset($data['references'])) { - $references = " REFERENCES " . $data['references']['refTable'] . '(' .$data['references']['refColumn'] . ')'; + $references = " REFERENCES " . $data['references']['refTable'] . '(' . $data['references']['refColumn'] . ')'; } if (isset($data["not_null"]) && $data["not_null"]) { - $constraint .=" NOT NULL"; + $constraint .= " NOT NULL"; } $type = $data['type']; if (!$pg) { - if (strtolower($type)=="serial") { + if (strtolower($type) == "serial") { $type = "integer"; } - //if (strtolower($type)=="boolean") - // $type = "integer"; } - return $name." ".$type.$references.$constraint; + return $name . " " . $type . $references . $constraint; } - public function addColumn($table_name, $column_name, $field) + /** + * Добавляет столбец в таблицу + * @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); + $q = "ALTER TABLE " . $table_name . " ADD COLUMN " . + $this->columnDefinition($column_name, $field, $pg); $this->db->query($q); } - public function getConstraintDef($c/*: array*/) + /** + * Возвращает определение ограничения + * @param array{fields: string[], type: string} $c + * @return string + */ + public function getConstraintDef(array $c) { if ($c['type'] == 'unique') { return "UNIQUE(" . implode(", ", $c['fields']) . ")"; @@ -177,8 +300,15 @@ class Manager return ""; } - //CreateTableQuery('users',['id'=>['type'=>'integer','constraint'=>'PRIMARY KEY']]) - public function createTableQuery($table, $fields, $constraints) + + /** + * Создает таблицу + * @example createTableQuery('users',['id'=>['type'=>'integer','constraint'=>'PRIMARY KEY']]) + * @param string $table + * @param array $fields + * @param array{fields: array, type: string}|string|null $constraints + */ + public function createTableQuery($table, $fields, $constraints): void { $pg = $this->db->isPostgres(); if ($constraints) { @@ -197,19 +327,24 @@ class Manager $this->db->query($statement); } + /** + * Возвращает дамп таблицы + * @param string $table_name + * @return array + */ public function dumpTable($table_name) { $pg = $this->db->isPostgres(); $result = []; - $data = $this->db->fetchAllArray("SELECT * FROM ".$table_name.";"); + $data = $this->db->fetchAllArray("SELECT * FROM " . $table_name . ";"); if (!$pg) { $table_fields = $this->tableInfo($table_name); foreach ($table_fields as $name => $value) { $type = strtolower($value['type']); if ($type == "boolean") { - foreach ($data as &$row) { + foreach ($data as &$row) { if (isset($row[$name])) { $row[$name] = boolval($row[$name]); } @@ -219,14 +354,18 @@ class Manager } foreach ($data as $r) { $result[] = [ - "type" => "insert", - "table_name" => $table_name, - "values" => $r + "type" => "insert", + "table_name" => $table_name, + "values" => $r ]; } return $result; } + /** + * Возвращает все имена таблиц + * @return list + */ public function getAllTableNames() { $result = []; @@ -242,6 +381,10 @@ class Manager return $result; } + /** + * Возвращает дамп всех таблиц + * @return array + */ public function dumpInserts() { $table_names = $this->getAllTableNames(); diff --git a/src/Database/PDOStatement.php b/src/Database/PDOStatement.php index 2a6a834..69a1c49 100644 --- a/src/Database/PDOStatement.php +++ b/src/Database/PDOStatement.php @@ -7,10 +7,16 @@ use ctiso\Database\StatementIterator, PDO; use TheSeer\Tokenizer\Exception; +/** + * @implements \IteratorAggregate + */ class PDOStatement extends \PDOStatement implements \IteratorAggregate { + /** @var int */ protected $cursorPos = 0; - public $cache = array(); + /** @var array */ + public $cache = []; + /** @var ?array */ public $fields; function getIterator(): \Iterator { @@ -20,11 +26,15 @@ class PDOStatement extends \PDOStatement implements \IteratorAggregate protected function __construct() { } - function rewind() { + function rewind(): void { $this->cursorPos = 0; } - public function seek($rownum) { + /** + * @param int $rownum + * @return bool + */ + public function seek($rownum): bool { if ($rownum < 0) { return false; } @@ -35,17 +45,19 @@ class PDOStatement extends \PDOStatement implements \IteratorAggregate return true; } - function valid() { + 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() { + function next(): bool{ if ($this->getRecordCount() > $this->cursorPos) { if (!isset($this->cache[$this->cursorPos])) { $this->cache[$this->cursorPos] = $this->fetch(PDO::FETCH_ASSOC); @@ -60,49 +72,86 @@ class PDOStatement extends \PDOStatement implements \IteratorAggregate } } - function key() { + function key(): int { return $this->cursorPos; } + /** + * @return mixed + */ function current() { return $this->cache[$this->cursorPos]; } + /** + * @return array|null + */ function getRow() { return $this->fields; } - function getInt($name) { + /** + * @param string $name + * @return int + */ + function getInt($name): int { if (!$this->fields) { throw new \Exception('no fields'); } return (int)$this->fields[$name]; } + /** + * @param string $name + * @return string + */ function getBlob($name) { return $this->fields[$name]; } + /** + * @param string $name + * @return string + */ function getString($name) { return $this->fields[$name] ?? null; } + /** + * @param string $name + * @return bool + */ function getBoolean($name) { return (bool)$this->fields[$name]; } + /** + * @param string $name + * @return mixed + */ function get($name) { return $this->fields[$name]; } + /** + * @param string $name + * @return array + */ function getArray($name) { return StringUtil::strToArray($this->fields[$name]); } + /** + * @return int + */ function getRecordCount() { return count($this->cache); } + /** + * @param array $args + * @return bool + */ function execute($args = null): bool { $result = parent::execute($args); return $result; diff --git a/src/Database/Statement.php b/src/Database/Statement.php index b0f6ebd..e270822 100644 --- a/src/Database/Statement.php +++ b/src/Database/Statement.php @@ -4,48 +4,80 @@ * Класс оболочка для PDOStatement для замены Creole */ namespace ctiso\Database; -use PDO, - ctiso\Database; + +use PDO; +use ctiso\Database; class Statement { + /** @var ?int */ protected $limit = null; + /** @var ?int */ protected $offset = null; + /** @var never */ protected $statement = null; - protected $binds = array(); + /** @var array{int|string, mixed, int}[] */ + protected $binds = []; + /** @var Database */ protected $conn; + /** @var string */ protected $query; - function __construct($query, $conn/*: Database*/) { + /** + * @param string $query + * @param Database $conn + */ + function __construct($query, $conn) { $this->query = $query; $this->conn = $conn; } - function setInt($n, $value) { + /** + * @param int|string $n + * @param int $value + */ + function setInt($n, $value): void { $this->binds [] = [$n, $value, PDO::PARAM_INT]; } - function setString($n, $value) { + /** + * @param int|string $n + * @param string $value + */ + function setString($n, $value): void { $this->binds [] = [$n, $value, PDO::PARAM_STR]; } - function setBlob($n, $value) { + /** + * @param int|string $n + * @param mixed $value + */ + function setBlob($n, $value): void { $this->binds [] = [$n, $value, PDO::PARAM_LOB]; } - function setLimit($limit) { + /** + * @param int $limit + */ + function setLimit($limit): void { $this->limit = $limit; } - function setOffset($offset) { + /** + * @param int $offset + */ + function setOffset($offset): void { $this->offset = $offset; } + /** + * @return PDOStatement + */ function executeQuery() { if ($this->limit) { $this->query .= " LIMIT {$this->limit} OFFSET {$this->offset}"; } - $stmt/*: PDOStatement*/ = $this->conn->prepare($this->query); + $stmt = $this->conn->prepare($this->query); foreach ($this->binds as $bind) { list($n, $value, $type) = $bind; $stmt->bindValue($n, $value, (int) $type); diff --git a/src/Database/StatementIterator.php b/src/Database/StatementIterator.php index f767408..43f82e9 100644 --- a/src/Database/StatementIterator.php +++ b/src/Database/StatementIterator.php @@ -3,14 +3,22 @@ namespace ctiso\Database; use PDO; +/** + * @implements \Iterator + */ class StatementIterator implements \Iterator { - + /** @var PDOStatement */ private $result; + /** @var int */ private $pos = 0; + /** @var int */ private $row_count; - public function __construct($rs/*: PDOStatement*/) { + /** + * @param PDOStatement $rs + */ + public function __construct($rs) { $this->result = $rs; $this->row_count = $rs->getRecordCount(); } @@ -34,15 +42,18 @@ class StatementIterator implements \Iterator return $this->result->cache[$this->pos]; } - function next(): void{ + function next(): void { $this->pos++; } - function seek($index) { + /** + * @param int $index + */ + function seek($index): void { $this->pos = $index; } - function count() { + function count(): int { return $this->row_count; } } diff --git a/src/Excel/DataTime.php b/src/Excel/DataTime.php index 877546d..7a910df 100644 --- a/src/Excel/DataTime.php +++ b/src/Excel/DataTime.php @@ -4,15 +4,19 @@ namespace ctiso\Excel; class DateTime { + /** @var int */ public $value; - function __construct($value) + /** + * @param int $value + */ + function __construct($value) { $this->value = (int)$value; } - function getString() + function getString(): string { return date('Y-m-d\TH:i:s.u', $this->value); - } + } } diff --git a/src/Excel/Document.php b/src/Excel/Document.php index d4d033e..4a9480c 100644 --- a/src/Excel/Document.php +++ b/src/Excel/Document.php @@ -8,11 +8,18 @@ use XMLWriter, Exception; class Document { + /** @var string */ static $ns = "urn:schemas-microsoft-com:office:spreadsheet"; - private $table = array (); - protected $styles = array(); + /** @var list */ + private $table = []; + /** @var array */ + protected $styles = []; - function addTable($table) { + /** + * Добавление таблицы в документ + * @param Table|callable $table Таблица или функция, возвращающая таблицу + */ + function addTable($table): void { $this->table [] = $table; } @@ -22,7 +29,7 @@ class Document { * @param array $values array Параметры стиля * @param string $type Тип стиля */ - function setStyle ($name, array $values, $type = 'Interior') + function setStyle ($name, array $values, $type = 'Interior'): void { if(!isset($this->styles[$name])) { $this->styles[$name] = []; @@ -33,7 +40,8 @@ class Document { /** * Генерация стилей */ - private function createStyles (XMLWriter $doc) { + private function createStyles (XMLWriter $doc): void + { $doc->startElement('Styles'); foreach ($this->styles as $name => $sn) { $doc->startElement('Style'); @@ -65,18 +73,22 @@ class Document { /** * Преобразует переводы строки в спец символы + * @param string $s + * @return string */ function clean ($s) { - assert(is_string($s)); - return strtr($s, ["\n" => " "]); } /** * Сохраняет таблицу в формате Office 2003 XML * http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats + * + * @param string $filename + * @throws Exception + * @return void */ - function save($filename) + function save($filename): void { $doc = new XMLWriter(); if (!$doc->openUri($filename)) { diff --git a/src/Excel/Number.php b/src/Excel/Number.php index 8ed12b4..3df78bb 100644 --- a/src/Excel/Number.php +++ b/src/Excel/Number.php @@ -4,16 +4,20 @@ namespace ctiso\Excel; class Number { + /** @var int */ public $value; - function __construct($value) + /** + * @param int|float $value + */ + function __construct($value) { $this->value = (int)($value); } - function getString() + function getString(): string { - return $this->value; - } + return (string) $this->value; + } } diff --git a/src/Excel/Table.php b/src/Excel/Table.php index ea28a2e..395253e 100644 --- a/src/Excel/Table.php +++ b/src/Excel/Table.php @@ -10,10 +10,16 @@ use XMLWriter, class TableCell { + /** @var string|false */ public $style = false; + /** @var string */ public $value; + /** @var bool */ public $merge = false; + /** + * @param string $value Значение клетки + */ function __construct ($value) { $this->value = $value; @@ -25,16 +31,29 @@ class TableCell */ class TableRow { + /** @var string|false */ public $style = false; - public $cells = array(); + /** @var TableCell[] */ + public $cells = []; + /** @var int|false */ public $height = false; - function setCell($y, $value) + /** + * Устанавливает значение для клетки + * @param int $y Номер столбца + * @param string $value Значение клетки + */ + function setCell($y, $value): void { $this->cells[$y] = new TableCell($value); } - function setCellStyle($y, $name) + /** + * Устанавливает стиль для клетки + * @param int $y Номер столбца + * @param string $name Имя стиля + */ + function setCellStyle($y, $name): void { $this->cells[$y]->style = $name; } @@ -45,12 +64,16 @@ class TableRow */ class Table { + /** @var int */ static $index; + /** @var string */ private $name; - private $style; - protected $rows = array(); + /** @var TableRow[] */ + protected $rows = []; + /** @var int|false */ protected $_splitVertical = false; + /** @var int|false */ protected $_splitHorizontal = false; function __construct() @@ -60,26 +83,30 @@ class Table /** * Записать значение в клетку с заданными координатами + * @param int $x Номер ряда + * @param int $y Номер столбца + * @param string $value Значение клетки */ - function setCell($x, $y, $value) + function setCell(int $x, int $y, $value): void { - assert(is_numeric($x) && $x > 0); - assert(is_numeric($y) && $y > 0); + assert($x > 0); + assert($y > 0); if(! isset($this->rows[$x])) { $this->rows[$x] = new TableRow(); } - $row/*: TableRow*/ = $this->rows[$x]; + + $row = $this->rows[$x]; $row->setCell($y, $value); } /** * Заполняет ряд начиная с указанного столбца значениями из массива */ - function setRow($row, $index, array $data) + function setRow(int $row, int $index, array $data): void { - assert(is_numeric($index) && $index > 0); - assert(is_numeric($row) && $row > 0); + assert($index > 0); + assert($row > 0); reset($data); for ($i = $index; $i < $index + count($data); $i++) { @@ -90,40 +117,40 @@ class Table /** * Устанавливает высоту ряда - * @param $row integer Номер ряда - * @parma $value real Высота ряда + * @param int $row Номер ряда + * @param int $value Высота ряда */ - function setRowHeight ($row, $value) + function setRowHeight (int $row, $value): void { - assert(is_numeric($row) && $row > 0); + assert($row > 0); $this->rows[$row]->height = $value; } /** * Устанавливает стиль ряда - * @param $row integer Номер ряда - * @parma $name string Имя стиля + * @param int $row Номер ряда + * @param string $name Имя стиля */ - function setRowStyle ($row, $name) + function setRowStyle(int $row, $name): void { - assert(is_numeric($row) && $row > 0); + assert($row > 0); $this->rows[$row]->style = $name; } /** * Обьединяет клетки в строке - * @param $row Номер ряда - * @param $cell Номер столбца - * @param $merge Количество клеток для обьединения + * @param int $x Номер ряда + * @param int $cell Номер столбца + * @param bool $merge Количество клеток для обьединения */ - function setCellMerge($x, $cell, $merge) + function setCellMerge(int $x, int $cell, $merge): void { - assert(is_numeric($x) && $x > 0); - assert(is_numeric($cell) && $cell > 0); + assert($x > 0); + assert($cell > 0); - $row/*: TableRow*/ = $this->rows[$x]; + $row = $this->rows[$x]; $row->cells[$cell]->merge = $merge; } @@ -133,18 +160,20 @@ class Table * @param int $y Номер столбца * @param string $name Имя стиля */ - function setCellStyle ($row, $y, $name) + function setCellStyle ($row, $y, $name): void { - if (isset($this->rows[$row])) + if (isset($this->rows[$row])) { $this->rows[$row]->setCellStyle($y, $name); + } } /** * Добавляет строку к таблице + * @return int Номер добавленной строки */ - function addRow($index = 1, array $data = [""]) + function addRow(int $index = 1, array $data = [""]) { - assert(is_numeric($index) && $index > 0); + assert($index > 0); $offset = $this->getRows() + 1; $this->setRow($offset, $index, $data); @@ -158,8 +187,9 @@ class Table */ function getRows() { - $keys/*: array*/ = array_keys($this->rows); - return max($keys); + // Высчитываем максимальный индекс, массив может быть разрежен поэтому используем array_keys + $keys = array_keys($this->rows); + return $keys === [] ? 0 : max($keys); } /** @@ -169,15 +199,15 @@ class Table */ function getRowCells(TableRow $row) { - $keys/*: array*/ = array_keys($row->cells); - return max($keys); + $keys = array_keys($row->cells); + return $keys === [] ? 0 :max($keys); } /** * Разделяет таблицу на две части по вертикали * @param int $n Количество столбцов слева */ - function splitVertical($n) { + function splitVertical($n): void { $this->_splitVertical = $n; } @@ -185,7 +215,7 @@ class Table * Разделяет таблицу на две части по горизонтали * @param int $n Количество столбцов сверху */ - function splitHorizontal($n) { + function splitHorizontal($n): void { $this->_splitHorizontal = $n; } @@ -196,9 +226,16 @@ class Table * @return int */ function getColumns() { - return max(array_map([$this, 'getRowCells'], $this->rows)); + $columns = array_map($this->getRowCells(...), $this->rows); + return $columns === [] ? 0 : max($columns); } + /** + * Кодирование строки + * @deprecated Можно заменить на значение + * @param string $s Строка + * @return string + */ function encode($s) { return $s; @@ -206,8 +243,13 @@ 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, $value/*: any*/, $setIndex) { + function createCell (TableCell $ncell, XMLWriter $doc, $j, mixed $value, $setIndex): void { $doc->startElement("Cell"); if ($ncell->style) { @@ -215,11 +257,11 @@ class Table } if ($ncell->merge) { - $doc->writeAttribute('ss:MergeAcross', $ncell->merge); + $doc->writeAttribute('ss:MergeAcross', (string)$ncell->merge); } if ($setIndex) { - $doc->writeAttribute('ss:Index', $j); + $doc->writeAttribute('ss:Index', (string)$j); } $doc->startElement("Data"); @@ -235,7 +277,7 @@ class Table } else { $doc->writeAttribute('ss:Type', "Number"); } - $doc->writeCdata($this->encode($value)); + $doc->writeCdata($value); } $doc->endElement(); $doc->endElement(); @@ -244,7 +286,7 @@ class Table /** * Генерация таблицы */ - public function createTable (XMLWriter $doc) { + public function createTable (XMLWriter $doc): void { $doc->startElement('Worksheet'); $doc->writeAttribute('ss:Name', $this->name); @@ -264,10 +306,10 @@ class Table } if ($this->rows[$i]->height) { - $doc->writeAttribute('ss:Height', $this->rows[$i]->height); + $doc->writeAttribute('ss:Height', (string)$this->rows[$i]->height); } - - $nrow/*: TableRow*/ = $this->rows[$i]; + /** @var TableRow $nrow */ + $nrow = $this->rows[$i]; // Флаг индикатор подстановки номера столбца $setIndex = false; for ($j = 1; $j <= $columns; $j++) { @@ -292,23 +334,23 @@ class Table $doc->endElement(); } - protected function splitPane (XMLWriter $doc) { + protected function splitPane (XMLWriter $doc): void { $doc->startElement('WorksheetOptions'); $doc->writeAttribute('xmlns', 'urn:schemas-microsoft-com:office:excel'); $doc->writeElement('FrozenNoSplit'); if ($this->_splitVertical) { - $doc->writeElement('SplitVertical', $this->_splitVertical); - $doc->writeElement('LeftColumnRightPane', $this->_splitVertical); + $doc->writeElement('SplitVertical', (string) $this->_splitVertical); + $doc->writeElement('LeftColumnRightPane', (string) $this->_splitVertical); } if ($this->_splitHorizontal) { - $doc->writeElement('SplitHorizontal', $this->_splitHorizontal); - $doc->writeElement('TopRowBottomPane', $this->_splitHorizontal); + $doc->writeElement('SplitHorizontal', (string) $this->_splitHorizontal); + $doc->writeElement('TopRowBottomPane', (string) $this->_splitHorizontal); } if ($this->_splitHorizontal && $this->_splitVertical) { - $doc->writeElement('ActivePane', (string)0); + $doc->writeElement('ActivePane', (string) 0); } else if($this->_splitHorizontal) { - $doc->writeElement('ActivePane', (string)2); + $doc->writeElement('ActivePane', (string) 2); } $doc->endElement(); } diff --git a/src/File.php b/src/File.php index c523cf6..8ee2eae 100644 --- a/src/File.php +++ b/src/File.php @@ -4,6 +4,11 @@ namespace ctiso; use Exception; class File { + /** + * @param string $filename + * @return string + * @throws Exception + */ static function getContents($filename) { $buffer = @file_get_contents($filename); if ($buffer !== false) { diff --git a/src/Filter/ActionAccess.php b/src/Filter/ActionAccess.php index 4fabc60..6dd44c3 100644 --- a/src/Filter/ActionAccess.php +++ b/src/Filter/ActionAccess.php @@ -1,20 +1,27 @@ processor = $processor; $this->user = $user; } @@ -23,12 +30,19 @@ class ActionAccess * Проверка доступных действий для пользователя * !! Реализация класса проверки действий не должна быть внутри Контроллера!!! * Информация о доступе может быть в файле, базе данных и т.д. + * + * @param string $action + * @return bool */ function checkAction($action) { // Импликация !! http://ru.wikipedia.org/wiki/Импликация return (!isset($this->access[$action]) || in_array($this->user->access, $this->access[$action])); } + /** + * @param HttpRequest $request + * @return mixed + */ function execute(HttpRequest $request) { $action = $request->getAction(); if(! $this->checkAction($action)) { diff --git a/src/Filter/ActionLogger.php b/src/Filter/ActionLogger.php index b2b02ff..ee94839 100644 --- a/src/Filter/ActionLogger.php +++ b/src/Filter/ActionLogger.php @@ -5,15 +5,25 @@ use ctiso\Role\UserInterface, ctiso\HttpRequest; /* Переделать формат Логов на список json */ -class ActionLogger -{ - public $before = array(); +class ActionLogger implements FilterInterface +{ + /** @var array */ + public $before = []; + /** @var resource */ public $file; + /** @var UserInterface */ public $user; + /** @var string */ public $action; + /** @var \ctiso\Controller\ActionInterface */ public $processor; - function __construct($processor/*: Filter*/, $logPath, $user/*: UserInterface*/) { + /** + * @param \ctiso\Controller\ActionInterface $processor + * @param string $logPath + * @param UserInterface $user + */ + function __construct($processor, $logPath, $user) { $this->processor = $processor; $this->user = $user; diff --git a/src/Filter/Authorization.php b/src/Filter/Authorization.php index 8510433..2421212 100644 --- a/src/Filter/Authorization.php +++ b/src/Filter/Authorization.php @@ -5,13 +5,20 @@ namespace ctiso\Filter; class Authorization { const SESSION_BROWSER_SIGN_SECRET = '@w3dsju45Msk#'; const SESSION_BROWSER_SIGN_KEYNAME = 'session.app.browser.sign'; + /** @var string */ public $group; + /** + * @param string $group + */ function __construct($group) { $this->group = $group; } - static function isLogged($group = 'access') { + /** + * @param string $group + */ + static function isLogged($group = 'access'): bool { // echo session_status(); if (session_status() == PHP_SESSION_NONE) { session_start(); @@ -29,7 +36,11 @@ class Authorization { return false; } - static function enter($id, $group = 'access') { + /** + * @param int $id + * @param string $group + */ + static function enter($id, $group = 'access'): void { // $db->executeQuery("UPDATE visitor SET sid = '' WHERE id_visitor = " . $result->getInt('id_user')); // session_register("access"); // session_register("time"); @@ -40,7 +51,7 @@ class Authorization { $_SESSION ["time"] = time(); } - static function getRawSign() + static function getRawSign(): string { $rawSign = self::SESSION_BROWSER_SIGN_SECRET; $signParts = ['HTTP_USER_AGENT']; @@ -48,17 +59,16 @@ class Authorization { foreach ($signParts as $signPart) { $rawSign .= '::' . ($_SERVER[$signPart] ?? 'none'); } - + return $rawSign; } - static function getBrowserSign() + static function getBrowserSign(): string { - return md5(self::getRawSign()); } - function logout() { + function logout(): void { session_destroy(); } } diff --git a/src/Filter/Filter.php b/src/Filter/Filter.php index 156d4e0..57d8e7c 100644 --- a/src/Filter/Filter.php +++ b/src/Filter/Filter.php @@ -5,29 +5,44 @@ */ namespace ctiso\Filter; -use ctiso\Controller\Action, - ctiso\HttpRequest; +use ctiso\Database; +use ctiso\HttpRequest; +use ctiso\Controller\ActionInterface; -class Filter +class Filter implements ActionInterface { + /** @var ActionInterface */ public $processor; - public function __construct($processor/*: Action*/) + + /** + * @param ActionInterface $processor + */ + public function __construct($processor) { $this->processor = $processor; } - + public function execute(HttpRequest $request) { return $this->processor->execute($request); } - public function getView($name, $class = 'ctiso\\View\\Top') + /** + * @param string $name + * @param class-string<\ctiso\View\View> $class + * @return \ctiso\View\View + */ + public function getView($name, $class = \ctiso\View\Top::class) { return $this->processor->getView($name, $class); } - public function getConnection() + public function getConnection(): Database { return $this->processor->getConnection(); } + + public function addUrlPart($key, $value): void { + $this->processor->addUrlPart($key, $value); + } } diff --git a/src/Filter/FilterInterface.php b/src/Filter/FilterInterface.php new file mode 100644 index 0000000..aee34a9 --- /dev/null +++ b/src/Filter/FilterInterface.php @@ -0,0 +1,12 @@ +getAction()) { // Авторизация по постоянному паролю case 'login': - $login = $request->get('login'); - $password = $request->get('password'); + $login = $request->getString('login', '') ; + $password = $request->getString('password'); $result = $this->role->getUserByLogin($login); // Поиск по логину if ($result) { @@ -60,6 +63,9 @@ class Login extends Filter $db = Database::getConnection($dsn); $user = $db->fetchOneArray("SELECT * FROM users WHERE login = :login", ['login' => $login]); + if ($user === false) { + return false; + } $userPassword = $user['password']; } /*else if (time() - $result->getInt('lastupdate') > 60*60*24*60) { // Проверить давность пароля, 60 дней @@ -67,7 +73,7 @@ class Login extends Filter $request->set('lastupdate', true); return false; }*/ - // Проверка на количества попыток авторизации + // Проверка на количества попыток авторизации $lastAttempt = $result; if ($lastAttempt->get('trie_count') >= self::AUTH_MAX_ATTEMPT /*&& time() - $lastAttempt['trie_time'] < self::AUTH_LAST_ATTEMPT_TIMER*/) { @@ -75,7 +81,7 @@ class Login extends Filter $request->set('timeout_error', true); break; } else { - $this->role->resetTries($request->get('login')); + $this->role->resetTries($request->getString('login')); } } // Извлечнеие пользователя из родительской CMS, для проверки пароля @@ -100,7 +106,7 @@ class Login extends Filter $result = $this->role->getUserByLogin($login); // Поиск по логину if ($result) { $temp = md5($result->getString('password') . $result->getString('login') . $result->getString('sid')); - if ($password == $temp) { + if ($password == $temp) { $this->enter($result); return true; } @@ -125,11 +131,15 @@ class Login extends Filter return false; } - private function enter($result) + /** + * Вход в систему + * @param PDOStatement $result + */ + private function enter($result): void { $this->user = $result; $random = rand(0, 1024 * 1024); - $this->role->setSID($random, $result); + $this->role->setSID((string)$random, $result); $_SESSION["group"] = $result->getInt('access'); $_SESSION["access"] = $result->getInt('id_user'); // id_user @@ -138,10 +148,13 @@ class Login extends Filter $_SESSION["time"] = time(); } - public function execute(HttpRequest $request) + /** + * @return mixed + */ + public function execute(HttpRequest $request): mixed { $logged = $this->isLoggin($request); - if ($request->get('action') == 'user_access') { + if ($request->getString('action') == 'user_access') { if ($logged) { $result = []; $result['fullname'] = $this->user->getString('patronymic') . " " . $this->user->getString('firstname'); @@ -153,7 +166,7 @@ class Login extends Filter } } - if ($request->get('action') == 'relogin') { + if ($request->getString('action') == 'relogin') { if ($logged) { return json_encode(['result' => 'ok', 'message' => "Авторизация успешна"]); } else { @@ -164,7 +177,7 @@ class Login extends Filter if (!$logged) { // Параметры при неправильной авторизации // Действия по умолчанию !! Возможно переход на форму регистрации - if ($request->get('mode') == 'ajax') { + if ($request->getString('mode') == 'ajax') { if (!$this->requestIsWhite($request)) { return json_encode(['result' => 'fail', 'message' =>"NOT_AUTHORIZED"]); } @@ -174,8 +187,11 @@ class Login extends Filter } } else if (isset($_SERVER['HTTP_REFERER'])) { $arr = []; - parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY) ?? '', $arr); - if (isset($arr['back_page']) && $request->get('mode') != 'ajax') { + 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') + { $request->redirect($arr['back_page']); } } @@ -184,19 +200,23 @@ class Login extends Filter return $text; } - /* --------------------- + /** * Проверка на попадание реквеста в белый список - */ - public function requestIsWhite(Collection $request) { + */ + public function requestIsWhite(Collection $request): bool { $module = $request->get('module'); $action = $request->get('action'); $moduleDir = explode('\\',$module)[0]; + // TODO: Параметр для белого списка перенести в install.json $file = Path::join($this->config->get('system', 'path'), 'modules', $moduleDir, 'filters', 'white.json'); if (file_exists($file)) { - $whiteList = json_decode(file_get_contents($file), true); - - if (in_array($action, $whiteList)) { + $text = file_get_contents($file); + if (!$text) { + return false; + } + $whiteList = json_decode($text, true); + if (is_array($whiteList) && in_array($action, $whiteList, true)) { return true; } } diff --git a/src/Form/CheckBox.php b/src/Form/CheckBox.php index 6eb1619..fd1fa2a 100644 --- a/src/Form/CheckBox.php +++ b/src/Form/CheckBox.php @@ -5,8 +5,9 @@ use ctiso\Form\Field; class CheckBox extends Field { + /** @var bool */ public $checked = false; - function setValue($value) + function setValue($value): void { $this->value = $value; $this->checked = $value; diff --git a/src/Form/Field.php b/src/Form/Field.php index c3df594..098003e 100644 --- a/src/Form/Field.php +++ b/src/Form/Field.php @@ -4,28 +4,51 @@ */ namespace ctiso\Form; +use ctiso\Form\OptionsFactory; + class Field { + /** @var bool */ public $hidden = false; + /** @var string */ public $name; + /** @var string */ public $label; // Метка поля + + /** @var mixed */ public $value; // Значение поля + /** @var string */ public $type = ""; // Каждому типу элемента соответствует макрос TAL - public $error_msg = null; - public $default = null; - public $error = false; + /** @var ?string */ + public $error_msg = null; + /** @var ?mixed */ + public $default = null; + /** @var bool */ + public $error = false; + /** @var bool */ public $require = false; + /** @var ?string */ public $hint = null; + /** @var ?int */ public $maxlength = null; + /** @var ?string */ public $fieldset = null; // Блоки (Убрать в отдельный класс!!!) - public $_title = array(); + /** @var array */ + public $_title = []; + /** @var string */ public $description = ""; - public $alias = array(); - - /** @phpstan-ignore-next-line */ + /** @var array */ + public $alias = []; + + + /** + * @param array $input + * @param OptionsFactory|null $factory + * @phpstan-ignore-next-line + */ public function __construct ($input = [], $factory = null) - { + { $this->default = null; if (isset($input['validate'])) { $this->require = strpos($input['validate'], 'require') !== false; @@ -41,12 +64,15 @@ class Field } } - function setValue($value/*: any*/) + /** + * @param mixed $value + */ + function setValue($value): void { $this->value = $value; } - function getId() + function getId(): string { return $this->name . '_label'; } diff --git a/src/Form/Form.php b/src/Form/Form.php index 30450d8..a425af1 100644 --- a/src/Form/Form.php +++ b/src/Form/Form.php @@ -5,29 +5,39 @@ */ namespace ctiso\Form; -use ctiso\Form\Field, - ctiso\Form\Select, - ctiso\Form\Input, - ctiso\View\View, - ctiso\Validator\Validator, - ctiso\HttpRequest; + +use ctiso\Form\Field; +use ctiso\Form\Select; +use ctiso\Form\Input; +use ctiso\Validator\Validator; +use ctiso\HttpRequest; /** * Форма для ввода */ class Form { + /** @var array */ public $field = []; //Поля формы + /** @var array */ public $fieldsets = []; //Группы полей (fieldset). Некоторые поля могут не принадлежать никаким группам + /** @var string */ public $action = ""; + /** @var string */ public $method = 'post'; + /** @var string */ public $header; + /** @var array */ protected $replace; + /** @var array */ protected $before; + /** @var array */ public $_title = []; + /** @var array */ public $alias = []; + /** @var class-string[] */ private $constructor = []; /** @@ -39,7 +49,6 @@ class Form { 'input' => Input::class, // input с проверкой на заполненность 'inputreq' => Input::class, - 'date' => Date::class, 'datereq' => Date::class, 'datetime' => DateTime::class, @@ -68,7 +77,7 @@ class Form { } - function getId() + function getId(): string { return '_form_edit'; } @@ -77,18 +86,18 @@ class Form { * Добавление конструкторя для поля формы * @param string $name Краткое название поля * @param class-string $class - * @return void */ - public function addFieldClass($name, $class) + public function addFieldClass($name, $class): void { $this->constructor [$name] = $class; } /** * Добавляет одно поле ввода на форму - * @return Field + * @param array{ type: string, name: string, hint?: string } $init + * @param OptionsFactory|null $factory */ - public function addField(array $init, $factory = null) + public function addField(array $init, $factory = null): Field { assert(isset($init['type'])); assert(isset($init['name'])); @@ -110,8 +119,7 @@ class Form { /** * Добавление fieldset на форму */ - - public function addFieldSet(array $fieldset) + public function addFieldSet(array $fieldset): void { $this->fieldsets[$fieldset['name']] = $fieldset; } @@ -119,8 +127,7 @@ class Form { /** * Добавление массива fieldset на форму */ - - public function addFieldSetList(array $list) + public function addFieldSetList(array $list): void { foreach ($list as $fieldset) { $this->addFieldSet($fieldset); @@ -130,8 +137,9 @@ class Form { /** * Добавляет список полей для формы * @param array $list + * @param OptionsFactory|null $factory */ - public function addFieldList(array $list, $factory = null) + public function addFieldList(array $list, $factory = null): void { foreach ($list as $init) { $this->addField($init, $factory); @@ -141,7 +149,7 @@ class Form { /** * Устанавливает ошибки после проверки */ - function setError(Validator $validator) + function setError(Validator $validator): void { foreach ($validator->getErrorMsg() as $name => $error) { @@ -150,7 +158,12 @@ class Form { } } - function setFieldError($name, $message) + /** + * Устанавливает ошибку для поля + * @param string $name + * @param string $message + */ + function setFieldError($name, $message): void { $this->field[$name]->error = true; $this->field[$name]->error_msg = $message; @@ -159,7 +172,7 @@ class Form { /** * Устанавливает значения из масива */ - function setValues(HttpRequest $request) { + function setValues(HttpRequest $request): void { foreach ($this->field as $key => $_) { $value = $request->getRawData($this->method, $key); $this->field[$key]->setValue($value); @@ -171,20 +184,28 @@ class Form { * @param object $data * @param array $schema Связь между элементами формы и свойствами обьекта */ - public function fill($data, array $schema) + public function fill($data, array $schema): void { foreach ($schema as $key => $conv) { list($value, $type) = $conv; - $this->field [$key]->setValue(call_user_func([\ctiso\Primitive::class, 'from_' . $type], $data->$value)); + $convertFn = [\ctiso\Primitive::class, 'from_' . $type]; + if (!is_callable($convertFn)) { + throw new \Exception('Не найден метод преобразования ' . $type); + } + $this->field[$key]->setValue(call_user_func($convertFn, $data->$value)); } } - public function set($name, $value) + /** + * @param string $name + * @param mixed $value + */ + public function set($name, $value): void { $this->field[$name]->setValue($value); } - function execute() + function execute(): self { return $this; } diff --git a/src/Form/Hidden.php b/src/Form/Hidden.php index 535e21b..6e3df61 100644 --- a/src/Form/Hidden.php +++ b/src/Form/Hidden.php @@ -4,5 +4,6 @@ namespace ctiso\Form; use ctiso\Form\Input; class Hidden extends Input { + /** @var bool */ public $hidden = true; } diff --git a/src/Form/OptionsFactory.php b/src/Form/OptionsFactory.php index 8921b56..79be2e0 100644 --- a/src/Form/OptionsFactory.php +++ b/src/Form/OptionsFactory.php @@ -3,5 +3,5 @@ namespace ctiso\Form; interface OptionsFactory { - function create(Select $field, array $options); + function create(Select $field, array $options): void; } \ No newline at end of file diff --git a/src/Form/QuestionType.php b/src/Form/QuestionType.php index a2e7ae0..3a3a9a5 100644 --- a/src/Form/QuestionType.php +++ b/src/Form/QuestionType.php @@ -5,7 +5,7 @@ use ctiso\Form\Select; class QuestionType extends Select { - function setValue($value) + function setValue($value): void { // Установить selected у options $this->value = $value; diff --git a/src/Form/Select.php b/src/Form/Select.php index 79fc2f4..89ea70d 100644 --- a/src/Form/Select.php +++ b/src/Form/Select.php @@ -3,12 +3,16 @@ namespace ctiso\Form; use ctiso\Form\Field; +/** + * @phpstan-type Option = array{value: string, name: string, selected?: bool, class?: string|false} + */ class Select extends Field { + /** @var Option[] */ public $options = []; /** - * @param array $input + * @param array{ options?: Option[], 'options.pair'?: array } $input * @param OptionsFactory $factory */ public function __construct ($input, $factory) { @@ -28,6 +32,11 @@ class Select extends Field } } + /** + * @param string[] $list + * @param bool $selected + * @return Option[] + */ public function optionsPair($list, $selected = false) { $result = []; foreach ($list as $key => $value) { diff --git a/src/Form/SelectMany.php b/src/Form/SelectMany.php index 86f4295..9ca752d 100644 --- a/src/Form/SelectMany.php +++ b/src/Form/SelectMany.php @@ -5,7 +5,7 @@ use ctiso\Form\Select; class SelectMany extends Select { - function setValue($value) + function setValue(mixed $value): void { // Установить selected у options if (!is_array($value)) { $value = [$value]; } diff --git a/src/Form/SelectOne.php b/src/Form/SelectOne.php index d2dc842..d631d81 100644 --- a/src/Form/SelectOne.php +++ b/src/Form/SelectOne.php @@ -8,7 +8,7 @@ use ctiso\Form\Select; class SelectOne extends Select { - function setValue($value) + function setValue($value): void { // Установить selected у options $this->value = $value; diff --git a/src/Form/TextArea.php b/src/Form/TextArea.php index 50ce417..01e60e0 100644 --- a/src/Form/TextArea.php +++ b/src/Form/TextArea.php @@ -1,13 +1,13 @@ value = $value; } diff --git a/src/Form/ViewState.php b/src/Form/ViewState.php index c9c6831..b315e6d 100644 --- a/src/Form/ViewState.php +++ b/src/Form/ViewState.php @@ -1,6 +1,6 @@ values[$name] = $value; } - function get($_rest) + /** + * Возвращает значение + * @param string ...$args + * @return mixed + */ + function get(...$args) { - $args = func_get_args(); $result = $this->values; foreach ($args as $name) { if (!isset($result[$name])) { @@ -28,16 +38,28 @@ class ViewState // extends Collection return $result; } - function saveState() + /** + * Сохраняет состояние + * @return string + */ + function saveState(): string { return base64_encode(serialize($this->values)); } - function restoreState($value) + /** + * Восстанавливает состояние + * @param string $value + */ + function restoreState($value): void { $this->values = unserialize(base64_decode($value)); } + /** + * Возвращает состояние + * @return array + */ function export() { return $this->values; diff --git a/src/Functions.php b/src/Functions.php index b547974..bd7fead 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -1,42 +1,55 @@ */ + protected $params; + /** @var callable */ protected $fn; - + + /** + * @param array $params + */ public function __construct($params) { $this->fn = array_shift($params); $this->params = $params; } - - function apply() { - $params = func_get_args(); + + /** + * Применение функции + * @param mixed ...$params + * @return mixed + */ + function apply(...$params) { array_splice($params, count($params), 0, $this->params); return call_user_func_array($this->fn, $params); - } + } } class left { - protected $params; + /** @var array */ + protected $params; + /** @var callable */ protected $fn; - + + /** + * @param array $params + */ public function __construct($params) { $this->fn = array_shift($params); $this->params = $params; } - function apply() { - $params = func_get_args(); + /** + * Применение функции + * @param mixed ...$params + * @return mixed + */ + function apply(...$params) { array_splice ($params, 0, 0, $this->params); return call_user_func_array ($this->fn, $params); } @@ -44,16 +57,25 @@ class left { define('__', '_ARGUMENT_PLACE_'); class partial { - protected $params; + /** @var array */ + protected $params; + /** @var callable */ protected $fn; - + + /** + * @param array $params + */ public function __construct($params) { $this->fn = array_shift($params); $this->params = $params; } - function apply() { - $params = func_get_args(); + /** + * Применение функции + * @param mixed ...$params + * @return mixed + */ + function apply(...$params) { $result = []; $count = count($this->params); for($i = 0, $j = 0; $i < $count; $i++) { @@ -66,19 +88,28 @@ class partial { } return call_user_func_array ($this->fn, $result); } -} +} /** * Композиция функций */ class compose { + /** @var array */ protected $fns; + + /** + * @param array $list + */ function __construct($list) { $this->fns = array_reverse($list); } - function apply () { - $params = func_get_args (); + /** + * Применение функций + * @param mixed ...$params + * @return mixed + */ + function apply (...$params) { $result = call_user_func_array($this->fns[0], $params); $count = count($this->fns); for ($i = 1; $i < $count; $i++) { @@ -90,48 +121,53 @@ class compose { class Functions { - static function partial($_rest) { - $closure = new partial(func_get_args()); + /** + * Частичное применение функции + * @param mixed ...$args + * @return mixed + */ + static function partial(...$args) { + $closure = new partial($args); return [$closure, 'apply']; } /** * Композиция функций - * @param array $_rest - * @return mixed + * @param mixed ...$args + * @return mixed */ - static function compose($_rest) { - $closure = new compose(func_get_args()); + static function compose(...$args) { + $closure = new compose($args); return [$closure, 'apply']; } /** * Карирование справа - * - * @return mixed + * @param mixed ...$args + * @return mixed */ - static function rcurry($_rest) { - $closure = new right(func_get_args ()); + static function rcurry(...$args) { + $closure = new right($args); return [$closure, 'apply']; } /** * Карирование слева - * - * @return mixed + * @param mixed ...$args + * @return mixed */ - static function lcurry($_rest) { - $closure = new left(func_get_args ()); + static function lcurry(...$args) { + $closure = new left($args); return [$closure, 'apply']; } /** * Разделение массива на два по условию * @param mixed $pred Условие по которому разделяется массив - * @param array $lst + * @param array $lst * - * @return mixed + * @return mixed */ static function partition($pred, $lst) { $left = []; @@ -147,25 +183,33 @@ class Functions { } /** - * @param array $value - * @param string $name + * @deprecated + * @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 $key + * @param string|int $key * - * @return int + * @return int */ static function __cmp($a, $b, $key) { if ($a[$key] == $b[$key]) { @@ -173,75 +217,75 @@ 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; } - - // Сравнение по ключу массиве - static function __index($n, $key, $row) { - return ($row[$key] == $n); - } - - static function __div($x, $y) { - return $x / $y; - } - - static function __self($name, $o) { - return call_user_func([$o, $name]); - } - - static function concat(/* $args ...*/) { - $args = func_get_args(); + + /** + * @param string ...$args + * @return string + */ + static function concat(...$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|\ArrayIterator $array * @return mixed */ - static function key_values($key, $array/*: array|ArrayIterator*/) { + static function key_values($key, $array) { $result = []; - + foreach($array as $item) { $result[] = $item[$key]; } return $result; } - - static function key_values_object($key, $array/*: array|ArrayIterator*/) { + + /** + * @param string $key + * @param array|\ArrayIterator $array + * @return array + */ + static function key_values_object($key, $array) { $result = []; - + foreach($array as $item) { $result[] = $item->{$key}; } return $result; } - + + /** + * @param string $key + * @param string $value + * @param array>|\ArrayIterator $array + * @return array + */ static function assoc_key_values($key, $value, $array) { $result = []; foreach ($array as $item) { @@ -249,7 +293,12 @@ class Functions { } return $result; } - + + /** + * @param string $key + * @param array>|\ArrayIterator $array + * @return array + */ static function assoc_key($key, $array) { $result = []; foreach ($array as $item) { @@ -257,24 +306,44 @@ class Functions { } return $result; } - - static function _get($key, $value/*: any*/, $array/*: array*/) { + + /** + * Возвращает значение по ключу + * @param string $key + * @param mixed $value + * @param array $array + * @return mixed + */ + static function _get($key, $value, $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 $array Массив + * @param callable $callback Функция * @return bool */ static function every(array $array, $callback) { @@ -285,17 +354,14 @@ class Functions { } return true; } - + /** * Логическа операция || ко всем элементам массива - * @param array $array - * @param mixed $callback - * + * @param array $array Массив + * @param callable $callback Функция * @return mixed */ - static function some(array $array, $callback) { - assert(is_callable($callback)); - + static function some(array $array, callable $callback) { foreach ($array as $key => $value) { if (call_user_func($callback, $value) === true) { return $key; @@ -303,10 +369,15 @@ class Functions { } return false; } - - static function span($length, array $array) { - assert(is_int($length)); - + + /** + * Разбивает массив на массивы определенной длины + * @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) { @@ -314,54 +385,70 @@ class Functions { } return $result; } - - static function array_ref($data, $n) { + + /** + * Возвращает значение массива + * @param array $data Массив + * @param string|int $n Ключ + * @return mixed + */ + static function array_ref(array $data, string|int $n) { return $data[$n]; } - - static function call() { - $args = func_get_args(); + + /** + * Вызывает функцию с аргументами + * @param mixed ...$args Аргументы + * @return mixed + */ + static function call(...$args) { $name = array_shift($args); return call_user_func_array($name, $args); } - + /** * Поиск элемента в массиве - * @param mixed $cb сравнение с элементом массива + * @param callable $cb сравнение с элементом массива * @param array $hs массив в котором ищется значение + * @param bool $strict * * @return int|string|null ключ найденого элемента в массиве */ static function array_usearch($cb, array $hs, $strict = false) { foreach($hs as $key => $value) { - if (call_user_func_array($cb, [$value, $key, $strict])) return $key; + if (call_user_func_array($cb, [$value, $key, $strict])) { + return $key; + } } return null; - } - + } + /** * Выбирает все сроки из таблицы с уникальными значениями ключа - * @param string $name Имя ключа - * @param array $table Двухмерный массив * @example * key_unique_values ('name', array (array ('name' => 1), array ('name' => 2), array ('name' => 1))) - => array (1, 2) - * @end example + * => array (1, 2) + * + * @param string $name Имя ключа + * @param array $table Двухмерный массив + * @return array Массив с уникальными значениями ключа */ static function key_unique_values ($name, $table) { - // Ищем уникальные значения для заданного ключа + // Ищем уникальные значения для заданного ключа $keys = []; foreach ($table as $row) { - if (!in_array ($row[$name], $keys)) + if (!in_array ($row[$name], $keys)) { $keys[] = $row[$name]; + } } return $keys; } - + /** * Сортировка двумерного массива по заданному ключу * @param array $array Массив * @param string $key Имя ключа по значению которого будет идти сравнение + * @param callable $fn Функция сравнения * @return array Отсортированный массив */ static function sortOn($array, $key, $fn = '\\ctiso\\Functions::__cmp') { @@ -371,16 +458,18 @@ class Functions { } /** - * Преобразует ключи элементов для многомерного массива + * Преобразует ключи элементов для многомерного массива + * @param string $key_name Имя ключа + * @param array $array Многомерный массив * @return mixed */ - static function hash_key ($key_name,$array/*: array */) { + static function hash_key ($key_name, $array) { $result = []; foreach($array as $value) { - $result[$value[$key_name]] = $value; + $result[$value[$key_name]] = $value; } - return $result; + return $result; } } diff --git a/src/HttpRequest.php b/src/HttpRequest.php index c8c0f7c..a24e60f 100644 --- a/src/HttpRequest.php +++ b/src/HttpRequest.php @@ -4,18 +4,19 @@ * Неверный запрос */ namespace ctiso; -use Exception, - ArrayAccess, - ctiso\Collection, - ctiso\Session; + + +use ctiso\Collection; +use ctiso\Session; /** - * @template T + * @template T=mixed */ class HttpRequest extends Collection { - + /** @var Session */ public $_session; + /** * Constructor * Stores "request data" in GPC order. @@ -41,21 +42,51 @@ class HttpRequest extends Collection parent::set('files', $data); } + /** + * @param string $key + * @return Collection + */ function _get($key) { return parent::get($key); } /** - * @param T $key - * @return mixed + * @param string $key + * @param mixed $default + * @return null|string|array */ function get($key, $default = null) { return parent::get('data')->get($key, $default); } - function session(Session $value = null) + 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 { if ($value) { $this->_session = $value; @@ -63,19 +94,25 @@ class HttpRequest extends Collection return $this->_session; } - function set($key, $value/*: any*/) + function set(string $key, mixed $value): void { parent::get('data')->set($key, $value); } - function export($key = 'data') + /** + * @param string $key + * @return array + */ + function export(string $key = 'data') { return parent::get($key)->export(); } - function clear() + function clear(): void { - return parent::get('data')->clear(); + /** @var Collection */ + $collection = parent::get('data'); + $collection->clear(); } /** @@ -85,7 +122,7 @@ class HttpRequest extends Collection * @param string $key * @return mixed */ - public function getRawData($var, $key) + public function getRawData(string $var, $key) { $data = parent::get(strtolower($var)); if ($data) { @@ -94,6 +131,12 @@ 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)); @@ -102,28 +145,34 @@ class HttpRequest extends Collection } } - public function setAction($name) + /** + * @param string $name + */ + public function setAction($name): void { $this->setRawData('get', 'action', $name); } - public function getAction() + public function getAction(): string { $result = $this->getRawData('get', 'action'); return ($result) ? $result : 'index'; } - public function isAjax() + public function isAjax(): bool { return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'); } - public function redirect($url) { + /** + * @param string $url + */ + public function redirect($url): void { header('location: ' . $url); exit(); } - static function getProtocol() { + static function getProtocol(): string { return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://"; } } diff --git a/src/Layout/Blank.php b/src/Layout/Blank.php index f731df3..8a0d0ff 100644 --- a/src/Layout/Blank.php +++ b/src/Layout/Blank.php @@ -9,6 +9,9 @@ use ctiso\Filter\Filter, class Blank extends Filter { + /** + * @return mixed + */ function execute(HttpRequest $request) { $text = $this->processor->execute($request); diff --git a/src/Layout/Manager.php b/src/Layout/Manager.php index 78a4686..26537b0 100644 --- a/src/Layout/Manager.php +++ b/src/Layout/Manager.php @@ -5,37 +5,44 @@ * Выбор оформления страницы осуществляется если было совпадение с каким либо условием */ namespace ctiso\Layout; -use ctiso\Filter\Filter, - ctiso\Functions, - ctiso\HttpRequest; + +use ctiso\Filter\Filter; +use ctiso\HttpRequest; class Manager extends Filter { - // Массив условий с их макетами - protected $condition = array(); + /** + * Массив условий с их макетами + * @var list + */ + protected $condition = []; /** * Функция которая добавляет условие для проверки параметров $_GET - * @param $get array() | true Ассоциативный массив ключей и значений для $_GET - * - * @example - * addConditionGet(array('module' => 'personal'), 'personal') - * addConditionGet(array('module' => 'login'), 'login') + * @param array|true $get Ассоциативный массив ключей и значений для $_GET + * @param Filter $layout Макет */ - public function addConditionGet($get, Filter $layout) + public function addConditionGet($get, Filter $layout): void { - $this->addCondition(Functions::rcurry([$this, 'checkGet'], $get), $layout); + $this->addCondition(fn(HttpRequest $request) => $this->checkGet($request, $get), $layout); } /** - * Условие для аякс запросов. Тоже самое что и addConditionGet но еще проверяется является ли запрос ajax + * Условие для аякс запросов. Тоже самое что и addConditionGet но еще проверяется является ли запрос ajax + * @param array|true $get Ассоциативный массив ключей и значений для $_GET + * @param Filter $layout Макет */ - public function addConditionXHR($get, Filter $layout) + public function addConditionXHR($get, Filter $layout): void { - $this->addCondition(Functions::rcurry([$this, 'checkXHR'], $get), $layout); + $this->addCondition(fn(HttpRequest $request) => $this->checkXHR($request, $get), $layout); } - public function checkGet($request/*: HttpRequest*/, $get) + /** + * @param HttpRequest $request + * @param array|true $get + * @return bool + */ + public function checkGet($request, $get) { if (is_array($get)) { foreach ($get as $key => $value) { @@ -47,37 +54,44 @@ class Manager extends Filter return true; } - public function checkXHR($request/*: HttpRequest*/, $get) + /** + * @param HttpRequest $request + * @param array|true $get + * @return bool + */ + public function checkXHR($request, $get): bool { return $request->isAjax() && $this->checkGet($request, $get); } /** * Добавляет условие в общем виде - * @parma $get function(HttpRequest) Функция - * @parma $layout Layout Макет + * @param callable $get Функция + * @param Filter $layout Макет */ - public function addCondition($get, Filter $layout) - { + public function addCondition($get, Filter $layout): void + { $this->condition [] = [$get, $layout]; } /** * Выбирает и применяет макет для страницы + * @return mixed */ - public function execute(HttpRequest $request) + public function execute(HttpRequest $request): mixed { 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->render(); + return $view->execute(); } else { return $view; } } } + return ''; } } diff --git a/src/Mail.php b/src/Mail.php index 7f3930d..848c645 100644 --- a/src/Mail.php +++ b/src/Mail.php @@ -4,26 +4,39 @@ * Класс для работы с почтой * 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; - private $encoding; + /** @var string */ + private $encoding; + /** @var string */ private $_notify = null; - protected $attachment = array (); + /** @var array */ + protected $attachment = array(); + /** @var string */ protected $uniqid; + /** @var string */ protected $type = "text/plain"; - function __construct() { + function __construct() + { $this->setEncoding("UTF-8"); $this->uniqid = strtoupper(uniqid((string)time())); // Идентефикатор разделителя } @@ -31,32 +44,35 @@ class Mail /** * Установка отправителя */ - function from($name) + function from(string $name): void { + // filter_var($name, FILTER_VALIDATE_EMAIL); $this->_from = $name; - } + } /** * Установка получателя */ - function to($name) // recipient + function to(string $name): void { $this->_to = $name; } - function replyTo($name) // recipient - { - } + /** + * @param string $name + */ + function replyTo($name): void + {} /** * Установка получателей копии */ - function copy($name) // recipient cc + function copy(string $name): void { $this->copy = $name; } - - function notify($notify) + + function notify(string $notify): void { $this->_notify = $notify; } @@ -64,59 +80,70 @@ class Mail /** * Тема письма */ - function subject($subject) + function subject(string $subject): void { - $this->_subject = $subject; + $this->_subject = $subject; } /** * Текст письма */ - function setContent($text) + function setContent(string $text): void { $this->content = $text; } - + /** * Кодировка текста в письме */ - function setEncoding($encoding) + function setEncoding(string $encoding): void { $this->encoding = $encoding; } /** * Добавление вложения из файла + * @param string $filename + * @param string|false $name */ - function addAttachment($filename, $name = false) + function addAttachment(string $filename, $name = false): void { - assert(is_string($filename)); - - if(file_exists($filename)) { // assert ?? + if (file_exists($filename)) { $file = fopen($filename, "rb"); if (is_resource($file)) { - $data = fread($file, filesize($filename)); - $this->attachment [] = ($name) ? [$data, $name] : [$data, basename($filename)]; + $size = filesize($filename); + if ($size !== false && $size > 0) { + $data = fread($file, $size); + $this->attachment[] = ($name) ? [$data, $name] : [$data, basename($filename)]; + } } } } - function setType($type) + /** + * Установка типа содержимого + * @param string $type + */ + function setType($type): void { $this->type = $type; } /** * Добавление вложения из строки с указанием имени файла + * @param string $data + * @param string $name */ - function addAttachmentRaw($data, $name) + function addAttachmentRaw($data, string $name): void { - assert(is_string($name)); - - $this->attachment [] = [$data, $name]; + $this->attachment[] = [$data, $name]; } - function quote($var, $val) + /** + * @param string $var + * @param string $val + */ + function quote($var, $val): string { return ";" . PHP_EOL . "\t" . $var . "=\"" . $val . "\""; } @@ -124,36 +151,40 @@ 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 = []) + function mimeTag($name, $value, array $args = []): string { - assert (is_string($name)); - assert (is_string($value)); + assert(is_string($name)); + assert(is_string($value)); - return $name . ": " . $value . implode("", array_map([$this, 'quote'], array_keys($args), array_values($args))) . PHP_EOL; + return $name . ": " . $value . implode("", array_map([$this, 'quote'], array_keys($args), array_values($args))) . PHP_EOL; } /** - * - * @see http://tools.ietf.org/html/rfc2047 + * + * @see http://tools.ietf.org/html/rfc2047 */ - function encodedWord($text, $encoding = 'B') + function encodedWord(string $text, string $encoding = 'B'): string { - return "=?{$this->encoding}?$encoding?".base64_encode($text)."?="; + return "=?{$this->encoding}?$encoding?" . base64_encode($text) . "?="; } /** * Тело сообщения */ - function getMessage() + function getMessage(): string { - $message = "--".$this->uniqid . PHP_EOL; + $message = "--" . $this->uniqid . PHP_EOL; $message .= $this->mimeTag("Content-Type", $this->type, ['charset' => $this->encoding]); $message .= $this->mimeTag("Content-Transfer-Encoding", "8bit"); $message .= PHP_EOL . $this->content . PHP_EOL . PHP_EOL; /* - * Вложения + * Вложения * http://tools.ietf.org/html/rfc2046#section-5.1.3 */ foreach ($this->attachment as $value) { @@ -171,7 +202,7 @@ class Mail /** * Заголовок сообщения */ - function getHeader() + function getHeader(): string { $head = $this->mimeTag("MIME-Version", "1.0"); $head .= $this->mimeTag("From", $this->_from); @@ -188,7 +219,7 @@ class Mail return $head; } - function getSubject() + function getSubject(): string { return $this->encodedWord($this->_subject); } @@ -196,27 +227,28 @@ class Mail /** * Вывод строки для сохранения в формате .eml */ - function eml() + function eml(): string { return "To: " . $this->_to . PHP_EOL . "Subject: {$this->getSubject()}" . PHP_EOL . $this->getHeader() . $this->getMessage(); } /** - * Отправка почты + * Отправка почты */ - function send() + function send(): bool { $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() { + function clear(): void + { foreach ($this->attachment as $key => &$value) { - unset($this->attachment[$key]); + unset($this->attachment[$key]); } $this->attachment = []; } diff --git a/src/MailAlt.php b/src/MailAlt.php index 7a42a7a..c9cae8a 100644 --- a/src/MailAlt.php +++ b/src/MailAlt.php @@ -5,8 +5,11 @@ use PHPMailer\PHPMailer\PHPMailer; class MailAlt { + /** @var PHPMailer */ public $mailer; + /** @var string */ public $_notify; + /** @var string */ public $encoding; function __construct() { @@ -17,62 +20,65 @@ class MailAlt /** * Установка отправителя */ - function from($name) + function from(string $name): void { $this->mailer->setFrom($name); - } + } /** * Установка получателя */ - function to($name) // recipient + function to(string $name): void // recipient { $this->mailer->addAddress($name); } - function replyTo($name) // recipient + function replyTo(string $name): void // recipient { - $this->mailer->AddReplyTo($name); + $this->mailer->addReplyTo($name); } /** * Установка получателей копии + * @param string $name */ - function copy($name) // recipient cc + function copy(string $name): void // recipient cc { $this->mailer->addCC($name); } - - function notify($notify) + + function notify(string $notify): void { $this->_notify = $notify; } /** * Тема письма + * @param string $subject */ - function subject($subject/*: string*/) + function subject(string $subject): void { - $this->mailer->Subject = $subject; + $this->mailer->Subject = $subject; } /** * Текст письма + * @param string $text */ - function setContent($text) + function setContent(string $text): void { $this->mailer->Body = $text; } - function setType($text) + function setType(string $text): void { $this->mailer->isHTML($text == 'text/html'); } - + /** * Кодировка текста в письме */ - function setEncoding($encoding) + function setEncoding(string $encoding): void { $this->encoding = $encoding; } @@ -80,20 +86,20 @@ class MailAlt /** * Добавление вложения из файла */ - function addAttachment($filename, $name = null) + function addAttachment(string $filename, ?string $name = null): void { $this->mailer->addAttachment($filename, $name); } /** - * Отправка почты + * Отправка почты */ - function send() + function send(): bool { return $this->mailer->send(); } - function eml() { + function eml(): string { return $this->mailer->getSentMIMEMessage(); } } diff --git a/src/Model/BaseMapper.php b/src/Model/BaseMapper.php index 7e55e72..37831a0 100644 --- a/src/Model/BaseMapper.php +++ b/src/Model/BaseMapper.php @@ -2,6 +2,9 @@ namespace ctiso\Model; +/** + * @property \ctiso\Database $db + */ abstract class BaseMapper { function getAllAsOptions(): array { return []; diff --git a/src/Model/Factory.php b/src/Model/Factory.php index ab560e3..6f6e840 100644 --- a/src/Model/Factory.php +++ b/src/Model/Factory.php @@ -1,17 +1,21 @@ db = $db; $this->config = $config; diff --git a/src/Numbers.php b/src/Numbers.php index 2494706..5d92be2 100644 --- a/src/Numbers.php +++ b/src/Numbers.php @@ -4,24 +4,27 @@ namespace ctiso; class Numbers { - static function roman($i) + static function roman(float $i): float { return 0; } - static function decimal($i) + static function decimal(float $i): float { return $i; } - static function prefix($prefix, array $array, $key = false) + /** + * @param array $array + * @return array + */ + static function prefix(callable $prefix, array $array) { $result = []; $count = count($array); for ($i = 0; $i < $count; $i++) { - $result [] = call_user_func($prefix, $i + 1) . '. ' . $array[$i]; + $result [] = call_user_func($prefix, $i + 1) . '. ' . $array[$i]; } return $result; } } - diff --git a/src/Path.php b/src/Path.php index e9d3c6b..55a0022 100644 --- a/src/Path.php +++ b/src/Path.php @@ -12,14 +12,16 @@ class Path { const SEPARATOR = "/"; - protected $path = array(); - protected $url = array(); - protected $absolute = false; + protected array $path = []; + protected array $url = []; + protected bool $absolute = false; + /** + * @param string $path Путь (Тип указан в doccomments т.к откудато приходит null) + */ public function __construct($path = '') { - //assert(is_string($path)); - $this->url = parse_url($path ?? ''); + $this->url = parse_url($path) ?: []; if (isset($this->url['path'])) { $path = $this->url['path']; @@ -36,16 +38,16 @@ class Path } } - static function factory($path) { + static function factory(string $path): Path { return new Path($path); } - public function getParts() + public function getParts(): array { return $this->path; } - public static function normalize($pathName) + public static function normalize(string $pathName): string { $path = new Path($pathName); return $path->__toString(); @@ -53,12 +55,12 @@ class Path /** * Базовое имя - * @param $path - * @return mixed + * @param string $path + * @return string */ public static function basename($path) { - $list = preg_split('#\\\\|/#s', $path); + $list = preg_split('#\\\\|/#s', $path) ?: ['']; return end($list); } @@ -73,6 +75,12 @@ class Path return pathinfo($fileName, PATHINFO_EXTENSION); } + /** + * Проверяет расширение файла + * @param string $fileName Полное имя файла + * @param string|array $extension Расширение файла + * @return bool + */ static function isType($fileName, $extension) { if (is_array($extension)) { @@ -84,17 +92,11 @@ class Path /** * Полное имя файла без расширения - * - * @param string $fileName Имя файла - * - * @return string */ - static function skipExtension($fileName) + static function skipExtension(string $fileName): string { - assert(is_string($fileName)); - $path = pathinfo($fileName); - if ($path['dirname'] === ".") { + if (!isset($path['dirname']) || $path['dirname'] === ".") { return $path['filename']; } else { return self::join($path['dirname'], $path['filename']); @@ -105,13 +107,10 @@ class Path * Возвращает имя файла без расширения * * @param string $fileName Полное имя файла - * * @return string */ - static function getFileName($fileName) + static function getFileName(string $fileName) { - assert(is_string($fileName)); - return pathinfo($fileName, PATHINFO_FILENAME); } @@ -121,20 +120,18 @@ class Path * Преобразует строку пути в массив * * @param string $path Путь - * - * @return array + * @return list */ - public static function listFromString($path) + public static function listFromString(string $path): array { - assert(is_string($path)); - $list = preg_split('#\\\\|/#s', $path); - - return $list; + return $list ?: []; } /** * Преобразует относительный путь в абсолютный + * @param array $path Путь + * @return array */ public static function optimize($path) // { @@ -154,8 +151,10 @@ class Path return $result; } - // Сравнение двух путей на равентство - public function equal($path/*: Path*/) + /** + * Сравнение двух путей на равентство + */ + public function equal(Path $path): bool { $count = count($this->path); if ($count == count($path->path)) { @@ -169,27 +168,29 @@ class Path return false; } - public static function makeUrl($path) + /** + * Преобразует путь в строку + * @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 { - $slash = (isset($path['host']) && (strlen($path['path']) > 0) && ($path['path'][0] != '/')) ? '/' : ''; + $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'] : '') . '@' : ''; - 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'] : ''); + $port = isset($path['port']) ? ':' . $path['port'] : ''; + $host = isset($path['host']) ? ('/' . $user . $path['host'] . $port) : ''; + + $query = isset($path['query']) ? '?' . $path['query'] : ''; + $fragment = isset($path['fragment']) ? '#' . $path['fragment'] : ''; + + return $scheme . $host . $slash . ($path['path'] ?? '') . $query . $fragment; } /** * Преобразует путь в строку - * - * @return string */ - public function __toString() + public function __toString(): string { $result = (($this->absolute) ? '/' : '') . implode(self::SEPARATOR, $this->path); $this->url['path'] = $result; @@ -199,11 +200,9 @@ class Path /** * Проверяет является ли папка родительской для другой папки * - * @parma Path $path - * - * @return boolean + * @param Path $path */ - public function isParent($path/*: Path*/) + public function isParent($path): bool { if (isset($this->url['host']) && isset($path->url['host']) && ($this->url['host'] != $path->url['host'])) return false; @@ -220,7 +219,7 @@ class Path return false; } - public static function _isParent($path1, $path2) + public static function _isParent(string $path1, string $path2): bool { $path = new Path($path1); return $path->isParent(new Path($path2)); @@ -230,7 +229,6 @@ class Path * Находит путь относительно текущего путя * * @param string $name Полный путь к файлу - * * @return string Относительный путь к файлу */ public function relPath($name) @@ -245,7 +243,12 @@ class Path return $path->__toString(); } - // Вычисляет относительный путь в виде строки + /** + * Вычисляет относительный путь в виде строки + * @param string $rpath Путь относительно которого вычисляется относительный путь + * @param string $lpath Путь к которому вычисляется относительный путь + * @return string Относительный путь + */ static function relative($rpath, $lpath) { // Нужно проверять диск!! $self = new Path($rpath); @@ -271,6 +274,10 @@ class Path return implode("/", $result); } + /** + * @param string $path + * @return string + */ public function append($path) { $base = $this->__toString(); @@ -280,11 +287,10 @@ class Path /** * Обьединяет строки в Path соединяя необходимым разделителем * fixme не обрабатывает параметры урла, решение Path::join(SITE_WWW_PATH) . '?param=pampam' + * @param string ...$args * @return string */ - static function fromJoin($_rest) { - $args = func_get_args(); - + static function fromJoin(...$args) { $result = []; $parts0 = new Path(array_shift($args)); $result [] = $parts0->getParts(); @@ -302,29 +308,30 @@ class Path /** * Обьединяет строки в строку пути соединяя необходимым разделителем * fixme не обрабатывает параметры урла, решение Path::join(SITE_WWW_PATH) . '?param=pampam' + * @param string ...$args * @return string */ - static function join($_rest) + static function join(...$args) { - $args = func_get_args(); $path = call_user_func_array([self::class, "fromJoin"], $args); return self::makeUrl($path->url); } // Проверка структуры имени файла - static function checkName($name, $extension) + static function checkName(string $name, string $extension): bool { return (strlen(pathinfo($name, PATHINFO_FILENAME)) > 0) && (pathinfo($name, PATHINFO_EXTENSION) == $extension); } - static function isCharName($char) + static function isCharName(string $char): bool { $ch = ord($char); return ((($ch >= ord('a')) && ($ch <= ord('z'))) || (strpos('-._', $char) !== false) || (($ch >= ord('0')) && ($ch <= ord('9')))); } // Проверка имени файла - static function isName($name) { + static function isName(string $name): bool + { if (strlen(trim($name)) == 0) { return false; } @@ -336,7 +343,7 @@ class Path return true; } - public function isAbsolute() + public function isAbsolute(): bool { return $this->absolute; } @@ -345,7 +352,6 @@ class Path * Подбирает новое временное имя для файла * * @param string $dst Предпологаемое имя файла - * * @return string Новое имя файла */ static function resolveFile($dst) @@ -365,12 +371,12 @@ class Path /** * Список файлов в директории * - * @param array $allow массив расширений для файлов - * @param array $ignore массив имен пааок которые не нужно обрабатывать + * @param ?string[] $allow массив расширений для файлов + * @param string[] $ignore массив имен пааок которые не нужно обрабатывать * - * @returnarray + * @return string[] */ - public function getContent($allow = null, $ignore = []) + public function getContent(?array $allow = null, array $ignore = []) { $ignore = array_merge([".", ".."], $ignore); return self::fileList($this->__toString(), $allow, $ignore); @@ -379,12 +385,12 @@ class Path /** * Обьединяет строки в путь соединяя необходимым разделителем * - * @param array $allow массив расширений разрешеных для файлов - * @param array $ignore массив имен пааок которые не нужно обрабатывать + * @param ?string[] $allow массив расширений разрешеных для файлов + * @param string[] $ignore массив имен папок которые не нужно обрабатывать * - * @return array + * @return string[] */ - function getContentRec($allow = null, $ignore = []) + function getContentRec(?array $allow = null, array $ignore = []) { $result = []; $ignore = array_merge([".", ".."], $ignore); @@ -392,8 +398,16 @@ class Path return $result; } - // Использовать SPL ??? - protected static function fileList($base, &$allow, &$ignore) + /** + * Список файлов в директории + * + * @param string $base Базовый путь + * @param ?string[] $allow массив расширений для файлов + * @param string[] $ignore массив имен папок которые не нужно обрабатывать + * + * @return string[] + */ + protected static function fileList(string $base, ?array &$allow, array &$ignore): array { if ($base == '') $base = '.'; $result = []; @@ -418,7 +432,7 @@ class Path return $result; } - protected static function fileListAll(&$result, $base, &$allow, &$ignore) + protected static function fileListAll(array &$result, string $base, ?array &$allow, array &$ignore): void { $files = self::fileList($base, $allow, $ignore); foreach ($files as $name) { @@ -438,7 +452,7 @@ class Path * * @return void */ - static function prepare($dst, $filename = true) + static function prepare(string $dst, bool $filename = true) { if ($filename) { $path_dst = pathinfo($dst, PATHINFO_DIRNAME); @@ -459,7 +473,7 @@ class Path * * @return string */ - static function updateRelativePathOnFileMove($relativePath, $srcFile, $dstFile) { + static function updateRelativePathOnFileMove(string $relativePath, string $srcFile, string $dstFile) { $srcToDst = self::relative($srcFile, $dstFile); return self::normalize(self::join($srcToDst, $relativePath)); } @@ -474,7 +488,7 @@ class Path * * @return string */ - static function updateRelativePathOnDirectoryMove($relativePath, $fileDir, $srcDir, $dstDir) { + static function updateRelativePathOnDirectoryMove(string $relativePath, string $fileDir, string $srcDir, string $dstDir) { $relativePath = self::normalize($relativePath); $fileDir = self::normalize($fileDir); $srcDir = self::normalize($srcDir); diff --git a/src/Primitive.php b/src/Primitive.php index 283d7e1..ce1482a 100644 --- a/src/Primitive.php +++ b/src/Primitive.php @@ -3,51 +3,74 @@ /** * Преобразование типов !!! Пересмотреть использование классов!! * Класс преобразования типа значения поля класса в тип поля таблицы - * @package system */ namespace ctiso; class Primitive { - // varchar - public static function to_varchar($value) + /** + * @param mixed $value + */ + public static function to_varchar($value): string { return ((string) $value); } + /** + * @param mixed $value + * @return mixed + */ public static function from_varchar($value) { return $value; } - // int - public static function to_bool($value) + /** + * @param mixed $value + */ + public static function to_bool($value): bool { - return filter_var($value, FILTER_VALIDATE_BOOLEAN);//(int)((bool) $value); + return filter_var($value, FILTER_VALIDATE_BOOLEAN); } - public static function from_bool($value) + /** + * Преобразование значения в булевое значение + * @param string $value + * @return bool + */ + public static function from_bool($value): bool { return ((bool) $value); } - // int - public static function to_int($value) + /** + * Преобразование значения в целое число + * @param string $value + * @return int + */ + public static function to_int($value): int { return ((int) $value); } - public static function from_int($value) + /** + * @param mixed $value + */ + public static function from_int($value): string { return ((string) $value); } - - // date - public static function to_date($value) + + /** + * Преобразование даты dd/mm/yy в unix timestamp + * @param string $value + * @return int + */ + public static function to_date($value): int { $result = 0; $tmp = explode("/", $value ?? '', 3); - + if (count($tmp) != 3) { return $result; } @@ -58,29 +81,39 @@ class Primitive { if ($month != 0 && $day != 0 && $year != 0) { if (checkdate($month, $day, $year)) { - return mktime(0, 0, 0, $month, $day, $year); + return mktime(0, 0, 0, $month, $day, $year) ?: 0; } else { return 0; } } - + return $result; } - public static function to_datetime($value) + /** + * Преобразование даты ISO 8601 в unix timestamp + * @param string $value + * @return int + */ + public static function to_datetime($value): int { $result = 0; $tmp = []; if (preg_match('/(\d+)-(\d+)-(\d+)T(\d+):(\d+)Z/', $value, $tmp)) { if (checkdate((int)$tmp[2], (int)$tmp[3], (int)$tmp[1])) { - $result = mktime((int)$tmp[4], (int)$tmp[5], 0, (int)$tmp[2], (int)$tmp[3], (int)$tmp[1]); + $result = mktime((int)$tmp[4], (int)$tmp[5], 0, (int)$tmp[2], (int)$tmp[3], (int)$tmp[1]) ?: 0; } } return $result; } - public static function from_date($value) + /** + * Преобразование даты в формат dd/mm/yyyy + * @param int $value + * @return string + */ + public static function from_date($value): string { if ($value > 0) { return date("d/m/Y", $value); @@ -88,7 +121,12 @@ class Primitive { return ''; } - public static function from_datetime($value) + /** + * Преобразование даты в формат ISO 8601 + * @param int $value + * @return string + */ + public static function from_datetime($value): string { if ($value > 0) { return date("Y-m-d\TH:i\Z", $value); @@ -97,24 +135,45 @@ class Primitive { } - // secure + /** + * @deprecated + * @template T + * @param T $value + * @return T + */ public static function to_secure($value) { // Значение приабразуется во время сохранения в базе данных return $value; } + /** + * @deprecated + * @template T + * @param T $value + * @return T + */ public static function from_secure($value) { return $value; } - // array - public static function to_array($value) + /** + * Преобразование значения в массив + * @param mixed $value + * @return array + */ + public static function to_array($value) { return (is_array($value)) ? $value : []; } + /** + * @deprecated + * @template T + * @param T $value + * @return T + */ public static function from_array($value) { return $value; diff --git a/src/Process.php b/src/Process.php index a1728f5..ce1c177 100644 --- a/src/Process.php +++ b/src/Process.php @@ -2,28 +2,46 @@ namespace ctiso; -if (!function_exists('str_getcsv')) { - function str_getcsv($input, $delimiter = ",", $enclosure = '"', $escape = "\\") { - $fiveMBs = 1024; - $fp = fopen("php://temp/maxmemory:$fiveMBs", 'r+'); - $data = ''; - if (is_resource($fp)) { - fputs($fp, $input); - rewind($fp); - $data = fgetcsv($fp, 1000, $delimiter, $enclosure); - fclose($fp); - } - return $data; - } +/** + * 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; } -function process_exists($pid) { + +/** + * process_exists + * @param int $pid + * @return bool + */ +function process_exists($pid) +{ if (PHP_OS == 'WINNT') { - $processes = explode("\n", shell_exec("tasklist.exe /NH /FO CSV")); - foreach($processes as $process) { + $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 ($pid == $csv[1]) return true; + if ($csv && $pid == $csv[1]) return true; } } return false; @@ -32,13 +50,19 @@ function process_exists($pid) { } } -function create_single_proces($fpid, $fn) { +/** + * create_single_proces + * @param string $fpid + * @param callable $fn + * @return int + */ +function create_single_process($fpid, $fn) +{ if (file_exists($fpid)) { - print_r(realpath($fpid)); - if (process_exists(file_get_contents($fpid))) { - return 1; + if (process_exists((int)file_get_contents($fpid))) { + return 1; } - } + } call_user_func($fn); return 0; } diff --git a/src/Registry.php b/src/Registry.php index 21e08eb..5eda214 100644 --- a/src/Registry.php +++ b/src/Registry.php @@ -5,47 +5,58 @@ use ctiso\File, Exception; class Registry { - public $namespace = []; - public $data; + /** @var array */ + private array $namespace = []; - function importFile($namespace, $filePath = null) { + 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($namespace, $data = []) { + function importArray(string $namespace, array $data = []): void { if (isset($this->namespace[$namespace])) { $data = array_merge($this->namespace[$namespace]['data'], $data); - } + } $this->namespace[$namespace] = [ 'path' => null, 'data' => $data - ]; + ]; } - public function get($ns, $key) { + /** + * @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 getOpt($ns, $key) { + /** + * @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($ns, $key) { + public function has(string $ns, string $key): bool { return isset($this->namespace[$ns]['data'][$key]); } - function set($ns, $key, $value) { + function set(string $ns, string $key, mixed $value): void { $this->namespace[$ns]['data'][$key] = $value; } } diff --git a/src/Role/User.php b/src/Role/User.php index 3f5728e..78f2923 100644 --- a/src/Role/User.php +++ b/src/Role/User.php @@ -1,41 +1,47 @@ db = $db; $this->groups = $groups; } - public function setDB(Database $db) { + public function setDB(Database $db): void { $this->db = $db; } - public function getName() { + public function getName(): string { return $this->name; } + /** + * @return bool + */ function isLogged() { return \ctiso\Filter\Authorization::isLogged(); } - - public function getUserByQuery(Statement $stmt) + public function getUserByQuery(Statement $stmt): ?PDOStatement { $result = $stmt->executeQuery(); if ($result->next()) { @@ -44,32 +50,38 @@ class User implements UserInterface $this->id = $result->getInt('id_user'); $this->password = $result->getString('password'); $this->fullname = implode(' ', [ - $result->getString('surname'), - $result->getString('firstname'), + $result->getString('surname'), + $result->getString('firstname'), $result->getString('patronymic')]); return $result; } return null; } + /** + * @param PDOStatement $result + * @return string + */ function getUserPassword($result) { return $result->get('password'); } - public function getUserByLogin($login) + 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) { + if ($result) { $time = time(); $id = $this->id; - $this->db->executeQuery("UPDATE users SET lasttime = $time WHERE id_user = $id"); // Время входа + $this->db->executeQuery( + "UPDATE users SET lasttime = :time WHERE id_user = :id", + ['time' => $time, 'id' => $id]); // Время входа } return $result; } - public function getUserById($id) + public function getUserById(int $id): ?PDOStatement { $stmt = $this->db->prepareStatement("SELECT * FROM users WHERE id_user = ?"); $stmt->setInt(1, $_SESSION ['access']); @@ -77,25 +89,37 @@ class User implements UserInterface if ($result) { $lasttime = $result->getInt('lasttime'); $time = time(); - if ($time - $lasttime > self::LIFE_TIME) return null; // Вышло время сессии + if ($time - $lasttime > self::LIFE_TIME) return null; // Вышло время сессии $id = $this->id; } return $result; } - function setSID($random, $result) { - return $this->db->executeQuery("UPDATE users SET sid = '$random', trie_count = 0 WHERE id_user = " . $result->getInt('id_user')); + /** + * @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($login) { + function resetTries(string $login): void { $this->db->executeQuery( "UPDATE users SET trie_count = :count WHERE login = :login", ['count' => 0, 'login' => $login] ); } - function updateTries($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']] diff --git a/src/Role/UserInterface.php b/src/Role/UserInterface.php index 295e4ca..a69d9b7 100644 --- a/src/Role/UserInterface.php +++ b/src/Role/UserInterface.php @@ -2,11 +2,18 @@ namespace ctiso\Role; use ctiso\Database\Statement; +use ctiso\Database\PDOStatement; interface UserInterface { - function getUserByQuery(Statement $stmt); - function getUserByLogin($login); - function getUserById($id); - function getName(); - function setSID($random, $result); + 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); } \ No newline at end of file diff --git a/src/Security.php b/src/Security.php index ea25461..fa9bea0 100644 --- a/src/Security.php +++ b/src/Security.php @@ -3,7 +3,7 @@ namespace ctiso; class Security { - static function generatePassword($length = 9, $strength = 0) { + static function generatePassword(int $length = 9, int $strength = 0): string { $vowels = 'aeuy'; $consonants = 'bdghjmnpqrstvz'; if ($strength & 1) { @@ -18,7 +18,7 @@ class Security { if ($strength & 8) { $consonants .= '@#$%'; } - + $password = ''; $alt = time() % 2; for ($i = 0; $i < $length; $i++) { diff --git a/src/Session.php b/src/Session.php index 6eab118..ff2bba6 100644 --- a/src/Session.php +++ b/src/Session.php @@ -2,9 +2,9 @@ namespace ctiso; -class Session +class Session { - function get($key) + function get(string $key): mixed { if (isset($_SESSION[$key])) { return $_SESSION[$key]; @@ -12,26 +12,27 @@ class Session return null; } - function set($key, $value) + function set(string|array $key, mixed $value): void { if (is_array($key)) { - $_SESSION[strtolower(get_class($key[0]))][$key[1]] = $value; + $className = get_class($key[0]); + $_SESSION[strtolower($className ?: '')][$key[1]] = $value; } else { $_SESSION[$key] = $value; } } - function clean($key) + function clean(string $key): void { unset($_SESSION[$key]); } - function start() + function start(): void { @session_start(); } - function stop() + function stop(): void { session_destroy(); } diff --git a/src/Settings.php b/src/Settings.php index 1f38299..d6958c3 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -5,21 +5,30 @@ use ctiso\File, Exception; /** - * Класс реестра + * Класс реестра * Реестр организован как ассоциативный многомерный массив * array( 'name1' => parameters1, 'name2' => parameters1, ... ) * - * name1, name2 ... - Имена модулей + * name1, name2 ... - Имена модулей * parameters1, parameters1 - Массивы с параметрами модуля * Имя необходимо чтобы потом легко было удалить ненужные ветки дерева */ class Settings { + /** @var array */ public $data = []; + /** @var string */ protected $file; + /** @var string */ protected $format = 'php'; + /** @var bool */ protected $is_read = false; + /** + * Конструктор + * @param string $file Путь к файлу + * @param 'php'|'json'|false $format Формат файла + */ public function __construct ($file = null, $format = false) { $fileFormat = ['theme' => 'json']; @@ -33,7 +42,7 @@ class Settings * Чтение настроек из файла * @return Boolean */ - public function read() + public function read(): bool { if (!file_exists ($this->file)) { $this->is_read = true; @@ -58,18 +67,19 @@ class Settings /** * Запись ключа в реестр (Реестр это многомерный массив) + * @param array $key Путь к значению ключа + * @param mixed $value Значение + * @return void */ - public function writeKey(array $key, $value) + public function writeKey(array $key, $value) { -// assert(count($key) >= 1); $data = &$this->data; while (count($key) > 1) { $name = array_shift($key); $data = &$data[$name]; - } - -// assert(count($key) == 1); + } + $name = array_shift($key); if (is_array($value)) { if (!isset($data[$name])) { @@ -83,8 +93,10 @@ class Settings /** * Обновляет массив в соответствии со значением + * @param array $data Массив + * @param mixed $value Значение */ - protected function merge(array &$data, $value) + protected function merge(array &$data, $value): void { foreach ($value as $key => $subvalue) { if (is_array($subvalue)) { @@ -97,17 +109,23 @@ class Settings } /** - * Чтение ключа из реестра + * Чтение ключа из реестра * @param array $key Путь к значению ключа + * @return mixed */ public function readKey(array $key) { return $this->readKeyData($key, $this->data); } + /** + * Чтение ключа из реестра + * @param array $key Путь к значению ключа + * @param array $data + * @return mixed + */ protected function readKeyData(array $key, $data) { -// assert(count($key) >= 1); while (count($key) > 1) { $name = array_shift($key); if (isset($data[$name])) { @@ -115,9 +133,8 @@ class Settings } else { return null; } - } - -// assert(count($key) == 1); + } + $name = array_shift($key); if (isset($data[$name])) { return $data[$name]; @@ -129,9 +146,10 @@ class Settings /** * Чтение ключа из реестра (Собирает все ключи с определенным значением во всех модулях) * @param mixed $key Путь к значению ключа внутри модуля + * @return array */ public function readKeyList(...$key) - { + { $result = []; foreach ($this->data as $name => $value) { $output = $this->readKeyData($key, $value); @@ -142,13 +160,17 @@ class Settings return $result; } - public function removeKey($name) + /** + * Удаление ключа из реестра + * @param string $name Имя ключа + */ + public function removeKey($name): void { unset($this->data[$name]); } - public function removeNode(array $key) + public function removeNode(array $key): void { $data = &$this->data; while (count($key) > 1) { @@ -158,11 +180,11 @@ class Settings $name = array_shift($key); unset($data[$name]); } - + /** * Запись настроек в файл (Может переименовать в store) * - * @param File $file + * @param string $file * @return void */ public function write($file = null) @@ -177,28 +199,49 @@ class Settings $result = var_export($this->data, true); $result = ""; } - file_put_contents (($file) ? $file : $this->file, $result); + file_put_contents(($file) ? $file : $this->file, $result); } - public function set($key, $value) { + /** + * Установка значения ключа + * @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() { @@ -207,6 +250,8 @@ class Settings /** * Проверка наличия ключа + * @param string $name Ключ + * @return bool */ public function hasKey($name) { diff --git a/src/Setup.php b/src/Setup.php index 1aef431..e3538be 100644 --- a/src/Setup.php +++ b/src/Setup.php @@ -6,40 +6,65 @@ * $setup->set('target', 'dst'); * $setup->executeActions('install'); * - */ + */ namespace ctiso; use ctiso\Tools\SQLStatementExtractor; use ctiso\Path; +use ctiso\File; +use SimpleXMLElement; class FakeZipArchive { + /** @var string */ public $base; - function open($path) { + + function open(string $path): void { $this->base = $path; } + /** + * Возвращает содержимое файла + * @param string $file + * @return string + */ function getFromName($file) { - return file_get_contents(Path::join($this->base, $file)); + return File::getContents(Path::join($this->base, $file)); } } -class Setup +class Setup { - protected $actions = array(); - public $context = array(); + /** @var array */ + protected $actions = []; + /** @var array */ + public $context = []; + /** @var string */ protected $file; + /** @var string */ protected $action; + /** @var SimpleXMLElement */ protected $node; - protected $stack = array(); - + /** @var array */ + protected $stack = []; + + /** @var FakeZipArchive */ public $zip; + /** @var string */ public $target; + /** @var string */ public $source; + /** + * @param string $file + */ public function __construct($file) { $this->file = $file; - $this->node = simplexml_load_file($file); + $node = simplexml_load_file($file); + if ($node === false) { + throw new \RuntimeException("Can't load file $file"); + } + $this->node = $node; $this->target = ''; $this->source = ''; @@ -56,22 +81,29 @@ class Setup } /** - * Регистрация новых действия для установки + * Регистрация новых действия для установки + * @param string $name + * @param callable $action */ - public function registerAction($name, $action) + public function registerAction(string $name, $action): void { $this->actions[$name] = $action; } /** * Установка переменных для шаблона + * @param string $name + * @param mixed $value */ - public function set($name, $value) + public function set(string $name, $value): void { $this->context[$name] = $value; } - function replaceFn($matches) { + /** + * @return string + */ + function replaceFn(array $matches) { if (isset($this->context[$matches[2]])) { $v = $this->context[$matches[2]]; } else { @@ -84,14 +116,14 @@ class Setup return $v; } - public function fileContent($file, array $tpl) - { + public function fileContent(string $file, array $tpl): string + { $result = $this->zip->getFromName($file); $result = preg_replace_callback('/\{\{\s*(\*?)(\w+)\s*\}\}/', [$this, 'replaceFn'], $result); return $result; } - function callAction($name, array $attributes) + function callAction(string $name, array $attributes): void { if(isset($this->actions[$name])) { call_user_func_array($this->actions[$name], $attributes); @@ -100,6 +132,8 @@ class Setup /** * Заменяет переменные на их значения в строке + * @param array $match массив совпадения + * @return string */ function replaceVariable(array $match) { @@ -111,50 +145,55 @@ class Setup /** * Для всех аттрибутов заменяет переменные на их значения + * @param SimpleXMLElement $attributes аттрибуты + * @return array */ - function resolve($attributes) - { + function resolve(SimpleXMLElement $attributes): array + { $result = []; foreach ($attributes as $key => $value) { - $result [$key] = preg_replace_callback("/\\\${(\w+)}/", [$this, 'replaceVariable'], $value); + $result[$key] = preg_replace_callback("/\\\${(\w+)}/", $this->replaceVariable(...), $value); } return $result; } - + /** * Выполняет список действий если для действия не указан аттрибут when то оно выполняется всегда - * + * * @param string $action специальное действие + * @return void */ function executeActions($action = "install") { - $this->action = $action; + $this->action = $action; if ($this->stack[count($this->stack) - 1] === false) { return; } - $item/*: \SimpleXMLElement*/ = $this->stack[count($this->stack) - 1]; + /** @var \SimpleXMLElement */ + $item = $this->stack[count($this->stack) - 1]; $root = $item->children(); foreach ($root as $node) - { + { $attributes = $node->attributes(); array_push($this->stack, $node); $this->callAction($node->getName(), [$this->resolve($attributes)]); array_pop($this->stack); } - } - + } + /** - * Копирования файла + * Копирования файла * preserve - Не переписывать файл если он существует * template Файл является шаблоном подставить параметры до копирования * src Исходный файл * dst Новый файл - * + * * @param array{preserve?: string, template: string, src: string, dst: string} $attributes + * @return void */ public function copyFile(array $attributes) - { + { $path = $this->targetPath($attributes['dst']); if (!(file_exists($path) && isset($attributes['preserve']))) { @@ -164,9 +203,9 @@ class Setup /** * Создает символическую ссылку на папку/файл - * @param array{target: string, link: string} $attributes + * @param array{target: string, link: string} $attributes */ - public function makeLink(array $attributes) + public function makeLink(array $attributes): void { if (function_exists('symlink')) { symlink($attributes['target'], $attributes['link']); @@ -174,37 +213,37 @@ class Setup } /** - * Подключение файла установки + * Подключение файла установки * @param array{file: string} $attributes Имя подключаемого файла */ - public function includeFile(array $attributes) + public function includeFile(array $attributes): void { $file = basename($this->file) . "/" . $attributes['file']; $setup = new Setup($file); - $setup->context = $this->context; + $setup->context = $this->context; $setup->executeActions(); } - function targetPath($s) { + function targetPath(string $s): string { return $this->target . '/' . $s; } /** * Создает новую папку - * dst Имя папки - * - * @param array{dst:string} $attributes + * dst Имя папки + * + * @param array{dst:string} $attributes */ - public function makeDirectory(array $attributes) + public function makeDirectory(array $attributes): void { $path = $this->targetPath($attributes['dst']); if (!file_exists($path)) { mkdir($path); } - } + } - function testWhen(array $attributes) + function testWhen(array $attributes): void { if (!isset($attributes['test']) || $attributes['test'] == $this->action) { $this->executeActions($this->action); @@ -212,9 +251,11 @@ class Setup } /** - * Выполнение Списка SQL команд - */ - function batchSQLZip($conn/*: Database*/, $file) + * Выполнение Списка SQL команд из ZIP файла + * @param Database $conn + * @param string $file + */ + function batchSQLZip($conn, $file): void { $stmtList = SQLStatementExtractor::extract($this->zip->getFromName($file)); foreach ($stmtList as $stmt) { @@ -222,7 +263,12 @@ class Setup } } - static function batchSQL($conn/*: Database*/, $file) + /** + * Выполнение Списка SQL команд + * @param Database $conn + * @param string $file + */ + static function batchSQL($conn, $file): void { $stmtList = SQLStatementExtractor::extractFile($file); foreach ($stmtList as $stmt) { diff --git a/src/SortRecord.php b/src/SortRecord.php index 3c4fe48..84604ea 100644 --- a/src/SortRecord.php +++ b/src/SortRecord.php @@ -2,20 +2,23 @@ namespace ctiso; -class SortRecord +class SortRecord { - public $key; - public $mode; - public $order; + public string $key; + public int $order; - function __construct($key, $mode, $order) - { + function __construct(string $key, bool $order) + { $this->key = $key; $this->order = ((boolean)($order) === false) ? 1 : -1; - $this->mode = $mode; } - function compare($a, $b) + /** + * @template T + * @param array $a + * @param array $b + */ + function compare(array $a, array $b): int { if($a[$this->key] == $b[$this->key]) { return 0; @@ -23,7 +26,7 @@ class SortRecord return ($a[$this->key] > $b[$this->key]) ? $this->order : -$this->order; } - function compareKeys($a, $b) + function compareKeys(object $a, object $b): int { if($a->{$this->key} == $b->{$this->key}) { return 0; @@ -31,17 +34,17 @@ class SortRecord return ($a->{$this->key} > $b->{$this->key}) ? $this->order : -$this->order; } - function sort(&$list) + function sort(array &$list): bool { return usort($list, [$this, 'compare']); } - function sortKeys(&$list) + function sortKeys(array &$list): bool { return usort($list, [$this, 'compareKeys']); } - function group(&$list, $key, $types) + function group(array &$list, string $key, array $types): void { $groups = []; foreach ($types as $name) { @@ -59,6 +62,6 @@ class SortRecord foreach ($groups as $value) { $result = array_merge($result, $value); } - $list = $result; + $list = $result; } } diff --git a/src/TableTree.php b/src/TableTree.php index ba0ec60..d09eac3 100644 --- a/src/TableTree.php +++ b/src/TableTree.php @@ -4,26 +4,29 @@ * Преобразование дерева из модели Plain в массив массивов (Adjacency List) */ -/** - * Обходит таблицу как дерево - * $fn ($name, $index, $rows, $cc) - * $name Ключ уровня - * $index Значение ключа уровня - * $rows Все столбцы текущго уровня - * $cc Столбцы более низкого уровня - * - * @param Array $level Уровни вложенности - * @param array $table Таблица - * @param Function $fn Функция которая применяется к каждой ветке дерева - */ namespace ctiso; use ctiso\Functions; class TableTree { + /** + * Обходит таблицу как дерево + * $fn ($name, $index, $rows, $cc) + * $name Ключ уровня + * $index Значение ключа уровня + * $rows Все столбцы текущго уровня + * $cc Столбцы более низкого уровня + * + * @param array $level Уровни вложенности + * @param array $table Таблица + * @param callable $fn Функция которая применяется к каждой ветке дерева + * @return array + */ static function walk($level, $table, $fn) { - if (empty ($level)) return $table; + if (empty ($level)) { + return $table; + } $name = array_shift ($level); - + $keys = Functions::key_unique_values($name, $table); $data = []; foreach ($keys as $index) { diff --git a/src/Tales.php b/src/Tales.php index 6538d86..2e3529f 100644 --- a/src/Tales.php +++ b/src/Tales.php @@ -4,27 +4,30 @@ * Расширения для PHPTAL для отображения времени и даты */ namespace ctiso; -use PHPTAL_Php_TalesInternal, - ctiso\Controller\SiteInterface, - ctiso\Controller\Component, - ctiso\HttpRequest, - PHPTAL_Tales, - PHPTAL_TalesRegistry; + +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($expression, $nothrow = false) { + static public function date(string $expression, bool $nothrow = false): string + { return "ctiso\\Tales::phptal_date(".PHPTAL_Php_TalesInternal::path($expression).")"; } - static public function time($expression, $nothrow = false) { + 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($expression, $nothrow = false) + static public function component(string $expression, bool $nothrow = false): string { $s = PHPTAL_Php_TalesInternal::string($expression); return "ctiso\\Tales::phptal_component(" . $s . ")"; @@ -33,7 +36,7 @@ class Tales_Component implements PHPTAL_Tales class Tales_Assets implements PHPTAL_Tales { - static public function assets($expression, $nothrow = false) + static public function assets(string $expression, bool $nothrow = false): string { $s = PHPTAL_Php_TalesInternal::string($expression); return "ctiso\\Tales::phptal_asset(" . $s . ")"; @@ -41,45 +44,47 @@ class Tales_Assets implements PHPTAL_Tales } class Tales { - static $site/*: SiteInterface*/; + /** @var ?SiteInterface */ + static $site; - static function phptal_date ($e) { + static function phptal_date (int $e): string { return date("d.m.Y", $e); } - static function phptal_time ($e) { + static function phptal_time (int $e): string { return date("H:i", $e); } - static function phptal_asset($s) { + static function phptal_asset(string $s): string { self::$site->addStyleSheet($s); return ""; } /** * Функция подключения компонента + * @param string $expression + * @return string */ - static function phptal_component($expression) { + static function phptal_component($expression): string { $begin = floatval(microtime(true)); - $component/*: Component*/ = null; - + /** @var Component */ $component = self::$site->loadComponent($expression); - $req = new HttpRequest(); + $req = new HttpRequest(); $result = $component->execute($req); - + echo ""; return $result; } - static function register($site) { + static function register(?SiteInterface $site): void { self::$site = $site; /* Регистрация нового префикса для подключения компонента */ $tales = PHPTAL_TalesRegistry::getInstance(); - $tales->registerPrefix('component', ['ctiso\\Tales_Component', 'component']); - $tales->registerPrefix('date', ['ctiso\\Tales_DateTime', 'date']); - $tales->registerPrefix('time', ['ctiso\\Tales_DateTime', 'time']); - $tales->registerPrefix('assets', ['ctiso\\Tales_Assets', 'assets']); + $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']); } } diff --git a/src/Tools/Drawing.php b/src/Tools/Drawing.php index 306d914..8a7568e 100644 --- a/src/Tools/Drawing.php +++ b/src/Tools/Drawing.php @@ -2,6 +2,8 @@ namespace ctiso\Tools; +use GdImage; + class Drawing { const ALIGN_LEFT = "left"; @@ -10,26 +12,61 @@ class Drawing const ALIGN_CENTER = "center"; const ALIGN_RIGHT = "right"; - static function drawrectnagle(&$image, $left, $top, $width, $height, $rgb) + /** + * @param GdImage $image + * @param int $left + * @param int $top + * @param int $width + * @param int $height + * @param list> $rgb + */ + static function drawRectangle(GdImage &$image, int $left, int $top, int $width, int $height, array $rgb): void { $color = imagecolorallocate($image, $rgb[0], $rgb[1], $rgb[2]); + if ($color === false) { + throw new \RuntimeException("Can't allocate color"); + } $right = $left + $width; $bottom = $top + $height; - imageline($image, $left, $top, $right, $top, $color); - imageline($image, $right,$top, $right, $bottom, $color); + imageline($image, $left, $top, $right, $top, $color); + imageline($image, $right, $top, $right, $bottom, $color); imageline($image, $left, $bottom, $right, $bottom, $color); - imageline($image, $left, $top, $left, $bottom, $color); + imageline($image, $left, $top, $left, $bottom, $color); } /** * http://ru2.php.net/imagettftext + * + * @param GdImage $image + * @param int $size + * @param float $angle + * @param int $left + * @param int $top + * @param int $color + * @param string $font + * @param string $text + * @param int $max_width + * @param int $max_height + * @param string $align + * @param string $valign */ - static function imagettftextbox(&$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)); + 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)); $text_lines = explode("\n", $text); // Supports manual line breaks! $lines = []; @@ -47,6 +84,9 @@ class Drawing for ($i = 0; $i < $count; $i++) { $item = $words[$i]; $dimensions = imagettfbbox($size, $angle, $font, $current_line . ($first_word ? '' : ' ') . $item); + if ($dimensions === false) { + continue; + } $line_width = $dimensions[2] - $dimensions[0]; $line_height = $dimensions[1] - $dimensions[7]; @@ -102,20 +142,15 @@ class Drawing return $largest_line_height * count($lines); } - - function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0) + 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) - { + 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[2] - $bbox[0]); + $temp_x += $spacing + ($bbox !== false ? ($bbox[2] - $bbox[0]) : 0); } } } diff --git a/src/Tools/Image.php b/src/Tools/Image.php index a59642f..dd3ebd4 100644 --- a/src/Tools/Image.php +++ b/src/Tools/Image.php @@ -2,9 +2,15 @@ namespace ctiso\Tools; +use GdImage; + class Image -{ - static function load($uri) +{ + /** + * @param string $uri + * @return GdImage|false + */ + static function load($uri): GdImage|false { $e = strtolower(pathinfo($uri, PATHINFO_EXTENSION)); switch ($e) { @@ -12,24 +18,32 @@ class Image case 'jpeg': case 'jpg': return imagecreatefromjpeg($uri); case 'gif': return imagecreatefromgif($uri); } + return false; } - static function fit($image, $prewidth, $preheight, $force = true) + static function fit(GdImage $image, int $prewidth, int $preheight, bool $force = true): GdImage|false { $width = imagesx($image); $height = imagesy($image); - $percent = min($prewidth / $width, $preheight / $height); - if ($percent > 1 && !$force) $percent = 1; - $new_width = $width * $percent; - $new_height = $height * $percent; + $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)); $image_p = imagecreatetruecolor($new_width, $new_height); imagecopyresampled($image_p, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height); return $image_p; } - static function save($image, $uri) + /** + * @param GdImage $image + * @param string $uri + * @return bool + */ + static function save($image, $uri): bool { $e = strtolower(pathinfo($uri, PATHINFO_EXTENSION)); switch ($e) { @@ -37,5 +51,6 @@ class Image case 'png': imagepng($image, $uri); break; case 'gif': imagegif($image, $uri); break; } + return false; } } \ No newline at end of file diff --git a/src/Tools/SQLStatementExtractor.php b/src/Tools/SQLStatementExtractor.php index 771b52b..5041e69 100644 --- a/src/Tools/SQLStatementExtractor.php +++ b/src/Tools/SQLStatementExtractor.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information please see * . */ - + /** * Static class for extracting SQL statements from a string or file. * @@ -26,142 +26,157 @@ * @version $Revision: 1.5 $ * @package creole.util.sql */ + namespace ctiso\Tools; + use Exception; -class SQLStatementExtractor { - +class SQLStatementExtractor +{ + + /** @var string */ protected static $delimiter = ';'; - + /** * Get SQL statements from file. - * + * * @param string $filename Path to file to read. * @return array SQL statements */ - public static function extractFile($filename) { + public static function extractFile($filename) + { $buffer = file_get_contents($filename); if ($buffer !== false) { - return self::extractStatements(self::getLines($buffer)); + return self::extractStatements(self::getLines($buffer)); } throw new Exception("Unable to read file: " . $filename); } - + /** * Extract statements from string. - * + * * @param string $buffer * @return array */ - public static function extract($buffer) { + public static function extract($buffer) + { return self::extractStatements(self::getLines($buffer)); } - + /** * Extract SQL statements from array of lines. * - * @param array $lines Lines of the read-in file. - * @return array + * @param string[] $lines Lines of the read-in file. + * @return string[] SQL statements */ - protected static function extractStatements($lines) { - + protected static function extractStatements($lines) + { + $statements = []; $sql = ""; - - foreach($lines as $line) { - - $line = trim($line); - - if (self::startsWith("//", $line) || - self::startsWith("--", $line) || - self::startsWith("#", $line)) { - continue; - } - - if (strlen($line) > 4 && strtoupper(substr($line,0, 4)) == "REM ") { - continue; - } - $sql .= " " . $line; - $sql = trim($sql); + foreach ($lines as $line) { + $line = trim($line); - // 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 ( + self::startsWith("//", $line) || + self::startsWith("--", $line) || + self::startsWith("#", $line) + ) { + continue; } - return $statements; + + 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; } - + // // 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($check/*: string*/, $string) { + protected static function endsWith(string $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 + * 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. */ - protected static function substring($string, $startpos, $endpos = -1) { + protected static function substring(string $string, int $startpos, int $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 $len-1 || $endpos < $startpos) { - trigger_error("substring(), Endindex out of bounds must be $startpos $len - 1 || $endpos < $startpos) { + trigger_error("substring(), Endindex out of bounds must be $startpos 0) { // already in sub-array? $subarr[$in_subarr][] = $tok; if ('}' === substr($tok, -1, 1)) { // check to see if we just added last component @@ -21,7 +27,7 @@ class StringUtil { $in_subarr--; } } elseif ($tok[0] === '{') { // we're inside a new sub-array - if ('}' !== substr($tok, -1, 1)) { + if ('}' !== substr($tok, -1, 1)) { $in_subarr++; // if sub-array has more than one element $subarr[$in_subarr] = []; @@ -32,81 +38,131 @@ class StringUtil { } else { // not sub-array $val = trim($tok, '"'); // remove " (surrounding strings) // perform type castng here? - $res[] = $val; + $res[] = $val; } } return $res; } - //Нормализация строк на русском - static function normalizeRussian($str) { - $result = preg_replace('/\s+/',' ', $str); + /** + * Нормализация строк на русском + * @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); //е на ё + $result = preg_replace('/ё/', 'е', $str); //е на ё } return $result; } - //Проверка равенства двух строк на русском языке. - static function equalRussianCheck($str1,$str2) { + /** + * Проверка равенства двух строк на русском языке. + * @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 -*/ - static function compare_string_to_variants($str, $variants){ + * + * @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); + $founded = $founded || self::equalRussianCheck($variant, $str); } return $founded; } - static function mb_str_split($str) { - return preg_split('~~u', $str, -1, PREG_SPLIT_NO_EMPTY); + /** + * Разбивает строку на массив символов + * @param string $str + * @return array + */ + static function mb_str_split(string $str): array + { + return preg_split('~~u', $str, -1, PREG_SPLIT_NO_EMPTY) ?: []; } - static function mb_strtr($str, $from, $to) { + /** + * Заменяет символы в строке на символы из другой строки + * @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($st) { - $st = self::mb_strtr($st,"абвгдеёзийклмнопрстуфхъыэ !+()", "abvgdeeziyklmnoprstufh_ie_____"); - $st = self::mb_strtr($st,"АБВГДЕЁЗИЙКЛМНОПРСТУФХЪЫЭ", "ABVGDEEZIYKLMNOPRSTUFH_IE"); + 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" - ]); + " " => '_', + "." => '_', + "," => '_', + "?" => '_', + "\"" => '_', + "'" => '_', + "/" => '_', + "\\" => '_', + "%" => '_', + "#" => '_', + "*" => '_', + "ж" => "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) { + /** + * Проверяет, является ли строка кодированной + * @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); } -} \ No newline at end of file +} diff --git a/src/Tools/TemplateImage.php b/src/Tools/TemplateImage.php index ad9d8f0..cb0ad0d 100644 --- a/src/Tools/TemplateImage.php +++ b/src/Tools/TemplateImage.php @@ -3,13 +3,18 @@ /** * Формат для композиции изображений */ + namespace ctiso\Tools; + use ctiso\Tools\Drawing; +use GdImage; class TemplateImage { - static $listfiles = array('jpg' => 'jpeg', 'gif' => 'gif', 'png' => 'png', 'bmp' => 'wbmp'); - static $listfonts = array( + /** @var array */ + static array $listfiles = array('jpg' => 'jpeg', 'gif' => 'gif', 'png' => 'png', 'bmp' => 'wbmp'); + /** @var array */ + static array $listfonts = array( 'georgia' => 'georgia.ttf', 'georgiabd' => 'georgiab.ttf', 'georgiaz' => 'georgiaz.ttf', @@ -24,26 +29,28 @@ 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 $context = array(); - protected $data = array(); - protected $base = "c:\\windows\\fonts\\"; - protected $image; + protected array $context = []; + protected array $data = []; + protected string $base = "c:\\windows\\fonts\\"; + protected GdImage $image; + /** @var bool */ protected $_prepare = true; + /** @var bool */ public $debug = false; - public $resource; - public $filename; + public string $resource; + public string $filename; - function __construct ($template = null) + function __construct(?array $template = null) { -// assert(is_string($src)); if ($template) { $this->data = $template; } @@ -52,131 +59,165 @@ class TemplateImage /** * Путь к изображению */ - function resourcePath($path) + function resourcePath(string $path): void { - assert(is_string($path)); - $this->resource = $path; } /** * Путь у шрифтам */ - function fontPath($path) + function fontPath(string $path): void { - assert(is_string($path)); - $this->base = $path; } - function set($name, $value) + /** + * @param string $name + * @param mixed $value + */ + function set(string $name, $value): void { - assert(is_string($name)); - - $this->context['['.$name.']'] = $this->encode($value); + $this->context['[' . $name . ']'] = $value; } - function setImage($name) + function setImage(string $name): void { $this->filename = $name; $this->image = $this->imagefromfile($name); } - function setEmptyImage($width, $height) + /** + * Создает пустое изображение + * @param int<1, max> $width + * @param int<1, max> $height + */ + function setEmptyImage($width, $height): void { $this->image = imagecreatetruecolor($width, $height); } /** * Создает изображение из файла + * @param string $file + * @return GdImage|null */ - function imagefromfile($file) + function imagefromfile(string $file) { - assert(is_string($file)); - $suffix = pathinfo($file, PATHINFO_EXTENSION); if (array_key_exists($suffix, self::$listfiles)) { - return call_user_func('imagecreatefrom' . self::$listfiles[$suffix], $file); + $imageFn = 'imagecreatefrom' . self::$listfiles[$suffix]; + if (!is_callable($imageFn)) { + return null; + } + return call_user_func($imageFn, $file); } return null; } - function getFontFile($name) + function getFontFile(string $name): string { - assert(is_string($name)); - - if(array_key_exists(strtolower($name), self::$listfonts)) { + if (array_key_exists(strtolower($name), self::$listfonts)) { return $this->base . self::$listfonts[$name]; } return $this->base . 'arial.ttf'; } - function fontSuffix($style) + function fontSuffix(array $style): string { - 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 ""; } - function imageText($text, $value/*: \stdClass*/) + /** + * @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 { - assert(is_string($text)); - $text = strtr($text, $this->context); $size = $value->fontSize; $fontfile = $this->getFontFile($value->fontFamily . $this->fontSuffix($value->fontStyle)); $color = intval(substr($value->color, 1), 16); - if ($value->align[0]) { + if ($value->align[0]) { $align = Drawing::ALIGN_LEFT; - } elseif ($value->align[2]) { + } elseif ($value->align[2]) { $align = Drawing::ALIGN_RIGHT; } else { $align = Drawing::ALIGN_CENTER; } - if ($value->valign[0]) { + if ($value->valign[0]) { $valign = Drawing::ALIGN_TOP; - } elseif ($value->valign[1]) { + } elseif ($value->valign[1]) { $valign = Drawing::ALIGN_CENTER; } else { $valign = Drawing::ALIGN_BOTTOM; } - Drawing::imagettftextbox($this->image, $size, 0, $value->left, $value->top, $color, $fontfile, $text, - $value->width, $value->height, - $align, $valign); + Drawing::imagettftextbox( + $this->image, + $size, + 0, + $value->left, + $value->top, + $color, + $fontfile, + $text, + $value->width, + $value->height, + $align, + $valign + ); } /** * Перекодировка текста + * @deprecated Можно заменить encode($x) -> $x */ - function encode($text) + function encode(string $text): string { - assert(is_string($text)); - return $text; //iconv("WINDOWS-1251", "UTF-8", $text); + return $text; } - function setSize($new_width, $new_height) + /** + * @param int<1,max> $new_width + * @param ?int<1,max> $new_height + */ + function setSize(int $new_width, ?int $new_height = null): void { $width = imagesx($this->image); $height = imagesy($this->image); if ($new_height == null) { - $new_height = ceil($height * $new_width / $width); + $new_height = max(1, (int)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() { - if($this->_prepare) { + function prepare(): void + { + if ($this->_prepare) { $this->_prepare = false; foreach ($this->data as $value) { $this->imageText($value->text, $value); // break; @@ -187,10 +228,8 @@ class TemplateImage /** * Генерирует изображение из шаблона */ - function render($file = null) + function render(?string $file = null): string|bool { - assert(is_string($file) || is_null($file)); - $this->prepare(); if ($file == null) { diff --git a/src/UTF8.php b/src/UTF8.php index 5eb93a0..4eab203 100644 --- a/src/UTF8.php +++ b/src/UTF8.php @@ -2,13 +2,20 @@ namespace ctiso; -class UTF8 { - static function str_split($str, $split_length = 1) { - $split_length = (int) $split_length; - +class UTF8 +{ + /** + * @param string $str + * @param int $split_length + * @return list + */ + 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]; + preg_match_all('/.{' . $split_length . '}|[^\x00]{1,' . $split_length . '}$/us', $str, $matches); + + return $matches[0]; } -} \ No newline at end of file +} diff --git a/src/Url.php b/src/Url.php index f4393b0..83ce86b 100644 --- a/src/Url.php +++ b/src/Url.php @@ -3,25 +3,29 @@ namespace ctiso; class Url { - public $parts = []; - public $parent/*: Url*/; + /** @var array */ + public array $parts = []; + public ?Url $parent; - function __construct() { - } - - function setParent($parent) { + /** + * @param Url|null $parent + */ + function setParent($parent): void { $this->parent = $parent; } - function setQuery($parts) { + /** + * @param string[] $parts + */ + function setQuery(array $parts): void { $this->parts = $parts; } - function addQueryParam($key, $value) { + function addQueryParam(string $key, ?string $value): void { $this->parts[$key] = $value; } - function toString() { + function toString(): string { return '?' . http_build_query(array_merge($this->parts, $this->parent ? $this->parent->parts : [])); - } + } } \ No newline at end of file diff --git a/src/UserMessageException.php b/src/UserMessageException.php index 226eb75..1638288 100644 --- a/src/UserMessageException.php +++ b/src/UserMessageException.php @@ -6,7 +6,11 @@ namespace ctiso; class UserMessageException extends \Exception { + /** @var string */ public $userMessage; + /** + * @param string $message + */ public function __construct($message) { parent::__construct($message); $this->userMessage = $message; diff --git a/src/Validator/Rule/AbstractRule.php b/src/Validator/Rule/AbstractRule.php index 4ab8307..706d8b6 100644 --- a/src/Validator/Rule/AbstractRule.php +++ b/src/Validator/Rule/AbstractRule.php @@ -1,47 +1,57 @@ field = $field; $this->errorMsg = $errorMsg; } - - public function setName($field) + + public function setName(string $field): self { $this->field = $field; return $this; } - public function setErrorMsg($errorMsg) + public function setErrorMsg(?string $errorMsg): self { $this->errorMsg = $errorMsg; return $this; } - public function getErrorMsg() - { + public function getErrorMsg(): string + { return $this->errorMsg; } - public function isValid(Collection $container, $status = null) + /** + * @param Collection $container + * @param bool|null $status + * @return bool + */ + public function isValid(Collection $container, $status = null): bool { return true; } - function skipEmpty() { + function skipEmpty(): bool { return true; } - public function setContext($ctx) + /** + * @param RuleContext $ctx + */ + public function setContext($ctx): void { $this->ctx = $ctx; } diff --git a/src/Validator/Rule/Alpha.php b/src/Validator/Rule/Alpha.php index 06793fd..2f13230 100644 --- a/src/Validator/Rule/Alpha.php +++ b/src/Validator/Rule/Alpha.php @@ -4,17 +4,18 @@ * Проверка на число */ namespace ctiso\Validator\Rule; -use ctiso\Validator\Rule\AbstractRule, - ctiso\Collection; + +use ctiso\Validator\Rule\AbstractRule; +use ctiso\Collection; class Alpha extends AbstractRule { - public function getErrorMsg() - { + public function getErrorMsg(): string + { return "Поле должно содержать только буквы"; } - public function isValid(Collection $container, $status = null) + public function isValid(Collection $container, $status = null): bool { return ctype_alpha($container->get($this->field)); } diff --git a/src/Validator/Rule/Code.php b/src/Validator/Rule/Code.php index 1102766..30f6e5e 100644 --- a/src/Validator/Rule/Code.php +++ b/src/Validator/Rule/Code.php @@ -13,7 +13,7 @@ class Code extends AbstractRule return "Неправильно указан персональный код"; } - function checkCode($code): bool { + function checkCode(array $code): bool { foreach($code as $c) { if (empty($c)) { return false; diff --git a/src/Validator/Rule/Count.php b/src/Validator/Rule/Count.php index c346892..fa650a8 100644 --- a/src/Validator/Rule/Count.php +++ b/src/Validator/Rule/Count.php @@ -9,26 +9,30 @@ use ctiso\Validator\Rule\AbstractRule, class Count extends AbstractRule { - public $size = 1; - public $max = null; + public int $size = 1; + public ?int $max = null; - public function getErrorMsg() - { + public function getErrorMsg(): string + { return "Количество записей должно быть не менне {$this->size} и не более {$this->max}"; } - function not_empty($s) { + /** + * @param string $s + * @return bool + */ + function notEmpty($s): bool { return $s != ""; } - - public function isValid(Collection $container, $status = null) + + public function isValid(Collection $container, $status = null): bool { if (!$this->max) { $this->max = $this->size; - } - $count = count(array_filter(array_map('trim', - explode(";", $container->get($this->field))), [$this, 'not_empty'])); + } + $count = count(array_filter(array_map('trim', + explode(";", $container->get($this->field))), [$this, 'notEmpty'])); return $count >= $this->size && $count <= ((int)$this->max); } diff --git a/src/Validator/Rule/Date.php b/src/Validator/Rule/Date.php index fdfff71..a53975d 100644 --- a/src/Validator/Rule/Date.php +++ b/src/Validator/Rule/Date.php @@ -9,16 +9,16 @@ use ctiso\Validator\Rule\AbstractRule, class Date extends AbstractRule { - public function getErrorMsg() - { + public function getErrorMsg(): string + { return "Неверный формат даты"; } - - public function isValid(Collection $container, $status = null) + + public function isValid(Collection $container, $status = null): bool { $pattern = "/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})$/"; $matches = []; - return (preg_match($pattern, $container->get($this->field), $matches) + return (preg_match($pattern, $container->get($this->field), $matches) && checkdate((int)$matches[2], (int)$matches[1], (int)$matches[3])); } } diff --git a/src/Validator/Rule/Email.php b/src/Validator/Rule/Email.php index 22c1287..ebbdc17 100644 --- a/src/Validator/Rule/Email.php +++ b/src/Validator/Rule/Email.php @@ -9,12 +9,12 @@ use ctiso\Validator\Rule\AbstractRule, class Email extends AbstractRule { - public function getErrorMsg() + public function getErrorMsg(): string { return "Неверный формат электронной почты"; } - public function isValid(Collection $container, $status = null) + public function isValid(Collection $container, $status = null): bool { $emails = explode(",", $container->get($this->field)); foreach ($emails as $email) { diff --git a/src/Validator/Rule/EmailList.php b/src/Validator/Rule/EmailList.php index 2132d0b..068becf 100644 --- a/src/Validator/Rule/EmailList.php +++ b/src/Validator/Rule/EmailList.php @@ -9,20 +9,14 @@ use ctiso\Validator\Rule\AbstractRule, class EmailList extends AbstractRule { - public function getErrorMsg() - { + public function getErrorMsg(): string + { return "Неверный формат электронной почты"; } - public function isValid(Collection $container, $status = null) { - $user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\|\{\}~\']+'; - $doIsValid = '(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]\.?)+'; - $ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}'; - $ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}'; - + public function isValid(Collection $container, $status = null): bool { $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; diff --git a/src/Validator/Rule/FileName.php b/src/Validator/Rule/FileName.php index d45d1f8..5f4410f 100644 --- a/src/Validator/Rule/FileName.php +++ b/src/Validator/Rule/FileName.php @@ -7,12 +7,12 @@ use ctiso\Validator\Rule\AbstractRule, class FileName extends AbstractRule { - public function getErrorMsg() + public function getErrorMsg(): string { return 'Название файла может содержать только символы латиницы в нижнем регистре и цифры'; } - public function isValid(Collection $container, $status = null) + public function isValid(Collection $container, $status = null): bool { return Path::isName($container->get($this->field)); } diff --git a/src/Validator/Rule/IsFile.php b/src/Validator/Rule/IsFile.php index 5304718..37115f8 100644 --- a/src/Validator/Rule/IsFile.php +++ b/src/Validator/Rule/IsFile.php @@ -9,42 +9,42 @@ use ctiso\Validator\Rule\AbstractRule, class IsFile extends AbstractRule { - private $type = array(); - private $maxsize = 1024; + private array $type = []; + private int $maxsize = 1024; - function skipEmpty() { + function skipEmpty(): bool { return false; } - function setSize($size) { + function setSize(int $size): void { $this->maxsize = $size; } - function setType(array $type) { + function setType(array $type): void { $this->type = $type; } - public function isValid(Collection $container, $status = null) + public function isValid(Collection $container, $status = null): bool { 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; diff --git a/src/Validator/Rule/MatchRule.php b/src/Validator/Rule/MatchRule.php index d54f448..b0db64b 100644 --- a/src/Validator/Rule/MatchRule.php +++ b/src/Validator/Rule/MatchRule.php @@ -9,14 +9,16 @@ use ctiso\Validator\Rule\AbstractRule, class MatchRule extends AbstractRule { + /** @var string */ public $same; - public function getErrorMsg() - { + public function getErrorMsg(): string + { return "Поля не совпадают"; } - public function isValid(Collection $container, $status = null) { + public function isValid(Collection $container, $status = null): bool + { return (strcmp($container->get($this->field), $container->get($this->same)) == 0); } } diff --git a/src/Validator/Rule/Notnull.php b/src/Validator/Rule/Notnull.php index d9130da..2a146e2 100644 --- a/src/Validator/Rule/Notnull.php +++ b/src/Validator/Rule/Notnull.php @@ -6,16 +6,16 @@ use ctiso\Validator\Rule\AbstractRule, class Notnull extends AbstractRule { - function skipEmpty() { + function skipEmpty(): bool { return false; } - public function getErrorMsg() - { + public function getErrorMsg(): string + { return "Поле не должно быть пустым"; } - public function isValid(Collection $container, $status = null) + public function isValid(Collection $container, $status = null): bool { $data = $container->get($this->field); if (is_array($data)) { diff --git a/src/Validator/Rule/Numeric.php b/src/Validator/Rule/Numeric.php index a3c6a5a..1fe5b0d 100644 --- a/src/Validator/Rule/Numeric.php +++ b/src/Validator/Rule/Numeric.php @@ -9,12 +9,12 @@ use ctiso\Validator\Rule\AbstractRule, class Numeric extends AbstractRule { - public function getErrorMsg() - { + public function getErrorMsg(): string + { return "Значение поля должно быть числом"; } - public function isValid(Collection $container, $status = null) + public function isValid(Collection $container, $status = null): bool { return (is_numeric($container->get($this->field))); } diff --git a/src/Validator/Rule/PregMatch.php b/src/Validator/Rule/PregMatch.php index 3d400bb..71e4252 100644 --- a/src/Validator/Rule/PregMatch.php +++ b/src/Validator/Rule/PregMatch.php @@ -1,21 +1,20 @@ pattern,$container->get($this->field)); - } + public function isValid(Collection $container, $status = null): bool + { + return preg_match($this->pattern, $container->get($this->field)) !== false; + } } diff --git a/src/Validator/Rule/RuleContext.php b/src/Validator/Rule/RuleContext.php new file mode 100644 index 0000000..e5be2fc --- /dev/null +++ b/src/Validator/Rule/RuleContext.php @@ -0,0 +1,10 @@ + -1 && $hour < 24 && $minute > -1 && $minute < 60) { return true; } + return false; } - public function isValid(Collection $container, $status = null) + public function isValid(Collection $container, $status = null): bool { /** @var list */ $tmp = explode($this->split, $container->get($this->field), 2); diff --git a/src/Validator/Rule/Unique.php b/src/Validator/Rule/Unique.php index 81489c3..37dfccf 100644 --- a/src/Validator/Rule/Unique.php +++ b/src/Validator/Rule/Unique.php @@ -3,19 +3,20 @@ /** */ namespace ctiso\Validator\Rule; -use ctiso\Validator\Rule\AbstractRule, - ctiso\Collection; + +use ctiso\Validator\Rule\AbstractRule; +use ctiso\Collection; class Unique extends AbstractRule { - public function getErrorMsg() + public function getErrorMsg(): string { return $this->ctx->getMessage(); } - public function isValid(Collection $container, $status = null) + public function isValid(Collection $container, $status = null): bool { - return $this->ctx->isUnique($container->get($this->field), $status, $container); + return $this->ctx->isUnique($container->getString($this->field), $status, $container); } } diff --git a/src/Validator/Validator.php b/src/Validator/Validator.php index 8c08cbe..2225479 100644 --- a/src/Validator/Validator.php +++ b/src/Validator/Validator.php @@ -1,23 +1,31 @@ - /** * Проверка коллекции */ namespace ctiso\Validator; -use Exception, - ctiso\Validator\Rule\AbstractRule, - ctiso\Collection; +use Exception; +use ctiso\Validator\Rule\AbstractRule; +use ctiso\Validator\Rule\RuleContext; +use ctiso\Collection; +/** + * @phpstan-type Rule array{ + * validate?:string, // Описание правила см. формат правила ниже + * name:string, // Имя переменой для проверки + * context?:RuleContext + * } + */ class Validator { - protected $chain = []; // Массив правил - protected $errorMsg = []; // Массив ошибок + /** @var AbstractRule[] */ + protected array $chain = []; // Массив правил + /** @var array */ + protected array $errorMsg = []; // Массив ошибок /** * Поля по умолчанию - * @var array> + * @var array> */ protected $type = [ 'date' => Rule\Date::class, @@ -36,21 +44,27 @@ class Validator 'reg' => Rule\PregMatch::class, ]; + /** + * @param Rule[] $rules + */ function __construct($rules = []) { $this->addRuleList($rules); } - function addRuleType($name, $className) { + /** + * Добавление правила в список + * @param string $name + * @param class-string $className + */ + function addRuleType(string $name, string $className): void { $this->type[$name] = $className; } /** * Добавление списка правил в специальном формате - * array(array('name' => fieldname, 'validate' => ruletext), ...) - * fieldname - Имя переменой для проверки - * ruletext - Описание правила см. формат правила ниже + * @param Rule[] $input */ - public function addRuleList(array $input) + public function addRuleList(array $input): void { // Разбор правила проверки // Формат правила 'rule1|rule2,param1=value1|rule3,param1=value1,param2=value2' @@ -83,7 +97,7 @@ class Validator } } - public function addRule($rule/*:z any*/) { + public function addRule(array|AbstractRule $rule): void { if (is_array($rule)) { $this->chain = array_merge($this->chain, $rule); } else { @@ -91,7 +105,10 @@ class Validator } } - public function skip($rule/*z: AbstractRule*/, $container/*: Collection*/) // -> Rule_Abstract + /** + * @param AbstractRule $rule + */ + public function skip($rule, Collection $container): bool { if ($rule->skipEmpty()) { $data = $container->get($rule->field); @@ -103,17 +120,23 @@ class Validator return false; } - function reset() { + function reset(): void { $this->errorMsg = []; } - public function validate(Collection $container, $rule = null, $status = null) + /** + * @param Collection $container + * @param AbstractRule[]|null $rules + * @param bool|null $status + * @return bool + */ + public function validate(Collection $container, $rules = null, $status = null): bool { $fields = []; - if ($rule) { - $this->chain = $rule; + if ($rules) { + $this->chain = $rules; } - // $this->errorMsg = []; + foreach ($this->chain as $rule) { //echo $key; if (!in_array($rule->field, $fields) && !$this->skip($rule, $container) && !$rule->isValid($container, $status)) { @@ -125,22 +148,25 @@ class Validator return $this->isValid(); } - public function addError($name, $message) + public function addError(string $name, string $message): void { $this->errorMsg[$name] = $message; } - public function isError() + public function isError(): bool { return !empty($this->errorMsg); } - public function isValid() + public function isValid(): bool { return empty($this->errorMsg); } - public function getErrorMsg() + /** + * @return array + */ + public function getErrorMsg(): array { return $this->errorMsg; } diff --git a/src/View/Composite.php b/src/View/Composite.php index 046e18e..edfaafb 100644 --- a/src/View/Composite.php +++ b/src/View/Composite.php @@ -1,45 +1,46 @@ tal = new PHPTAL($file); $this->tal->setPhpCodeDestination(PHPTAL_PHP_CODE_DESTINATION); - $this->tal->setEncoding(PHPTAL_DEFAULT_ENCODING); + $this->tal->setEncoding(PHPTAL_DEFAULT_ENCODING); $this->tal->setTemplateRepository(PHPTAL_TEMPLATE_REPOSITORY); $this->tal->setOutputMode(PHPTAL::HTML5); $this->tal->stripComments(true); // $this->tal->addPreFilter(new PHPTAL_PreFilter_Normalize()); } - function set($key, $val) { + function set(string $key, mixed $val): void { if ($key == 'title') { $this->setTitle($val); } $this->tal->set($key, $val); } - function __set($key, $val) { + function __set(string $key, mixed $val): void { $this->tal->set($key, $val); } - function execute() + function execute(): string { $this->processChild(); return $this->tal->execute(); } - function setTranslator($t) { + function setTranslator(PHPTAL_TranslationService $t): void { $this->tal->setTranslator($t); } } diff --git a/src/View/JsonView.php b/src/View/JsonView.php new file mode 100644 index 0000000..3ba8fd4 --- /dev/null +++ b/src/View/JsonView.php @@ -0,0 +1,40 @@ +_name = $name; + } + + /** + * @param string $key + * @param mixed $value + */ + function set($key, $value): void { + $this->_data[$key] = $value; + } + + /** + * @param string $key + * @param mixed $value + */ + function __set($key, $value): void { + $this->_data[$key] = $value; + } + + /** + * @return string + */ + function execute() { + return json_encode($this->_data) ?: ''; + } +} diff --git a/src/View/Pages.php b/src/View/Pages.php index 48b2750..a64873d 100644 --- a/src/View/Pages.php +++ b/src/View/Pages.php @@ -8,6 +8,14 @@ namespace ctiso\View; class Pages { static int $range = 5; + + /** + * @param int $page номер страницы + * @param int $onpage количество страниц на странице + * @param int $count количество всех страниц + * @param string $prefix префикс + * @return array{'all': bool, 'list': array, 'first': string, 'last': string, 'next': string|false, 'prev': string|false} + */ static function getPages($page, $onpage, $count, $prefix = '?') { $n = ceil($count / $onpage); @@ -20,30 +28,30 @@ class Pages } return [ 'all' => ($n > 1), - 'list' => $result, - 'first' => self::href($prefix, $url . 1), - 'last' => self::href($prefix, $url . $n), - 'next' => ($page == $n)? false : self::href($prefix, $url . ($page + 1)) , + 'list' => $result, + 'first' => self::href($prefix, $url . 1), + 'last' => self::href($prefix, $url . $n), + 'next' => ($page == $n)? false : self::href($prefix, $url . ($page + 1)) , 'prev' => ($page == 1)? false : self::href($prefix, $url . ($page - 1))]; } /** * @deprecated - * @param $page int номер страницы - * @param $onpage int количество элем на странице + * @param int $page номер страницы + * @param int $onpage количество элем на странице * @return string */ - static function getLimit($page/*: number*/, $onpage/*: number*/) { + static function getLimit(int $page, int $onpage) { if ($page <= 0) { $page = 1; } return "LIMIT $onpage OFFSET " . ($page - 1) * $onpage; } /** - * @param $page int номер страницы - * @param $onpage int количество элем на странице + * @param int $page номер страницы + * @param int $onpage количество элем на странице * @return array */ - static function _getLimit($page, $onpage) { + static function _getLimit(int $page, int $onpage) { if ($page <= 0) { $page = 1; } return [ 'count' => $onpage, @@ -51,8 +59,13 @@ class Pages ]; } - static function href($prefix, $x) { - return $prefix . $x; + /** + * @param string $prefix префикс + * @param string $x строка + * @return string + */ + static function href($prefix, $x) { + return $prefix . $x; } } diff --git a/src/View/Plain.php b/src/View/Plain.php index 8ffc877..f070d3d 100644 --- a/src/View/Plain.php +++ b/src/View/Plain.php @@ -7,41 +7,70 @@ namespace ctiso\View; */ class Plain extends \stdClass { + /** @var string */ protected $document; - protected $values = array(); + /** @var array */ + protected $values = []; + /** + * Конструктор + * @param string $document шаблон + */ public function __construct ($document) { $this->document = $document; } - public function set($key, $value) + /** + * Установка значения + * @param string $key ключ + * @param mixed $value значение + */ + public function set($key, $value): void { $this->values[$key] = $value; } - public function import($list) + /** + * Импорт значений + * @param array $list список значений + */ + public function import($list): void { $this->values = array_merge($this->values, $list); } - public function __set($key, $value) + /** + * @param string $key ключ + * @param mixed $value значение + */ + public function __set($key, $value): void { $this->set($key, $value); } + /** + * Выполнение шаблона + * @return string + */ public function execute() { $result = $this->values; return self::getTemplateContent ($this->document, $result); } - static function getTemplateContent($document, $result) + /** + * Получение содержимого шаблона + * @param string $document шаблон + * @param array $result результат + * @return string содержимое шаблона + */ + static function getTemplateContent(string $document, $result): string { ob_start (); - include ($document); + include ($document); $content = ob_get_contents (); ob_clean (); - return $content; + return $content === false ? '' : $content; } } diff --git a/src/View/Top.php b/src/View/Top.php index 37b3339..2a7d7fe 100644 --- a/src/View/Top.php +++ b/src/View/Top.php @@ -8,16 +8,32 @@ class Top extends Composite { /** * Общая строка заголовка + * @var int */ public $mid = 0; - public $require = array(); - public $deps = array(); + /** @var array */ + public $require = []; + /** @var array */ + public $deps = []; + /** @var \ctiso\Registry */ + public $config; + /** + * Заголовок страницы + * + * @return string + */ public function getTitle() { return implode(" - ", array_filter($this->doTree('_title', false), [$this, 'isNotNull'])); } + /** + * Идентификатор + * + * @param string $pref + * @return string + */ function getId($pref) { $this->mid++; @@ -29,7 +45,8 @@ class Top extends Composite * * @return string */ - public function render() + #[\Override] + public function execute(): string { $this->doTree('alias'); @@ -68,11 +85,12 @@ class Top extends Composite } $init = []; + /** @var View $item */ + foreach ($s->_section as $key => $item) { - $ss /*: View*/= $item; - if ($ss->codeGenerator !== null) { + if ($item->codeGenerator !== null) { // функцию которая вычисляет а не результат - $part = call_user_func($ss->codeGenerator, $this, $key, $value); + $part = call_user_func($item->codeGenerator, $this, $key, $value); $init[] = $part; } } @@ -89,14 +107,14 @@ class Top extends Composite $this->set('title', $this->getTitle()); $this->set('jspath', $this->config->get('system', 'web')); - // - return $this->execute(); // execute+phptal ?? + // + return parent::execute(); // execute+phptal ?? } /** * Массив имен файлов скриптов * - * return array + * @return array */ public function getScripts() { @@ -113,6 +131,11 @@ class Top extends Composite return implode("\n", $this->doTree('_scriptstring')); } + /** + * Строка со скриптом + * + * @return string + */ public function getScriptStartup() { return implode("\n", $this->doTree('_startup')); @@ -121,11 +144,10 @@ class Top extends Composite /** * Массив имен файлов стилей * - * return array + * @return array */ public function getStyleSheet() { return $this->doTree('_stylesheet'); } - } \ No newline at end of file diff --git a/src/View/View.php b/src/View/View.php index 036cc72..7d7776e 100644 --- a/src/View/View.php +++ b/src/View/View.php @@ -5,24 +5,35 @@ use Exception; class View extends \stdClass { - protected $_section = array(); // Вложенные шаблоны - // Блоки - protected $_stylesheet = array(); // Массив стилей текущего шаблона - protected $_script = array(); // Массив скриптов текущего шаблона - public $_scriptstring = array(); - protected $_startup = array(); - protected $_values = array(); + /** @var array Вложенные шаблоны */ + protected array $_section = []; - protected $_title = null; // Заголовок текущего шаблона + /** @var string[] $_stylesheet Массив стилей текущего шаблона */ + protected array $_stylesheet = []; + /** @var string[] $_script Массив скриптов текущего шаблона */ + protected array $_script = []; + /** @var string[] $_scriptstring */ + public array $_scriptstring = []; + /** @var string[] $_startup */ + protected array $_startup = []; - public $active_module; - public $module_action; - public $prefix; + protected array $_values = []; - public $suggestions; //подсказки + protected ?string $_title = null; // Заголовок текущего шаблона - public $alias = []; - public $codeGenerator = null; + public ?string $active_module = null; + public string $module_action; + + /** @var string[] */ + public array $prefix; + + /** @var string[] */ + public array $suggestions = []; //подсказки + + /** @var array> */ + public array $alias = []; + // public $codeGenerator = null; + /** @var View|null */ public $parent_view = null; function __construct() { @@ -34,14 +45,17 @@ class View extends \stdClass * @param string $section переменная шаблона * @param View|string $view вложенный шаблон */ - public function setView($section, $view) + public function setView($section, $view): void { - $this->_section [$section] = $view; + $this->_section[$section] = $view; if (is_object($view)) { $view->parent_view = $this; } } + /** + * @param array $values + */ public function assignValues($values): void { $this->_values = $values; @@ -53,7 +67,7 @@ class View extends \stdClass * * @param string $name путь к скрипту */ - public function addScript($name): void + public function addScript(string $name): void { $output = $this->resolveName($this->alias, $name . '?' . http_build_query($this->prefix)); $this->_script [] = $output; @@ -62,18 +76,18 @@ class View extends \stdClass /** * Добавляет код скипта к текущему шаблону * - * @param string $name строка javascript кода + * @param string $code строка javascript кода */ - public function addScriptRaw($name, $startup = false): void + public function addScriptRaw(string $code, bool $startup = false): void { if ($startup) { - $this->_startup [] = $name; + $this->_startup [] = $code; } else { - $this->_scriptstring [] = $name; + $this->_scriptstring [] = $code; } } - public function setPrefix($name, $val) { + public function setPrefix(string $name, string $val): void { $this->prefix[$name] = $val; } @@ -82,7 +96,7 @@ class View extends \stdClass * * @param string $name путь к стилю */ - public function addStyleSheet($name) + public function addStyleSheet(string $name): void { $output = $this->resolveName($this->alias, $name . '?' . http_build_query($this->prefix)); $this->_stylesheet [] = $output; @@ -92,9 +106,9 @@ class View extends \stdClass * Рекурсивно извлекает из значение свойства обьекта * * @param string $list Имя свойства - * @param boolean $flatten + * @param bool $flatten */ - protected function doTree($list, $flatten = true) + protected function doTree($list, bool $flatten = true): array { $result = ($flatten == true) ? $this->$list : [$this->$list]; foreach ($this->_section as $value) { @@ -109,7 +123,15 @@ class View extends \stdClass return $result; } - /*abstract*/ public function set($key, $value) + /* + function getTitleArray(): array { + return array_reduce($this->_section, fn ($result, $item) => + is_object($item) ? array_merge($result, $item->getTitleArray()) : $result, []); + } + */ + + + /*abstract*/ public function set(string $key, mixed $value): void { } @@ -125,31 +147,35 @@ class View extends \stdClass /** * Установка заголовка шаблона - * - * @param string $title */ - public function setTitle($title): void + public function setTitle(string $title): void { $this->_title = $title; } - protected function isNotNull($title): bool + protected function isNotNull(?string $title): bool { return $title !== null; } + /** + * @param array> $alias + */ function setAlias($alias): void { $this->alias = $alias; } - function addAlias($name, $path): void + function addAlias(string $name, string $path): void { $this->alias[$name] = $path; $this->set($name, $path); } - function findFile($pathlist, string $file): string { + /** + * @param string[] $pathlist + */ + function findFile(array $pathlist, string $file): string { foreach($pathlist as $key => $www) { if (file_exists($key . '/' . $file)) { @@ -159,8 +185,12 @@ class View extends \stdClass throw new Exception("file not found: $file"); } - // FIXME: Префикс, конфликтует с протоколом - function resolveName($alias, $file) { + + /** + * FIXME: Префикс, конфликтует с протоколом + * @param string[]|string[][] $alias + */ + function resolveName($alias, string $file): string { list($type, $filename) = explode(":", $file, 2); // Сделать поиск а не просто замену папки при совпадении имени @@ -175,11 +205,23 @@ class View extends \stdClass return $file; } - public function resolveAllNames($alias, $list) { + /** + * @param string[][] $alias + * @param string[] $list + * @return string[] + */ + public function resolveAllNames($alias, array $list): array { $result = []; foreach($list as $item) { $result [] = $this->resolveName($alias, $item); } return $result; } + + /** + * Шаблон всегда возвращает строку + */ + function execute(): string { + return ''; + } } diff --git a/src/ZipFile.php b/src/ZipFile.php index 99ae70b..b6ecdf6 100644 --- a/src/ZipFile.php +++ b/src/ZipFile.php @@ -26,12 +26,15 @@ class ZipFile extends ZipArchive // Read all Files in Dir $dir = opendir($location); + if (!$dir) { + throw new \RuntimeException("Enable to open dir '$dir'"); + } while (($file = readdir($dir)) !== false) { if (in_array($file, $this->ignore)) continue; // Rekursiv, If dir: FlxZipArchive::addDir(), else ::File(); - $call = (is_dir($location . $file)) ? 'addDir' : 'addFile'; - call_user_func([$this, $call], $location . $file, $name . $file); + $call = (is_dir($location . $file)) ? $this->addDir(...) : $this->addFile(...); + call_user_func($call, $location . $file, $name . $file); } }