diff --git a/src/Collection.php b/src/Collection.php index cf8b814..430cc69 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -58,12 +58,23 @@ class Collection implements \ArrayAccess /** * @param string $key - * @param int $default + * @param int $default * @return int */ - public function getInt($key, $default = 0) + 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; } /** @@ -73,15 +84,59 @@ class Collection implements \ArrayAccess */ 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; } + /** + * Получает булево значение + * Поддерживает: 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($key, $default = 1) + public function getNat(string $key, int $default = 1): int { $result = (int)$this->get($key, $default); return (($result > 0) ? $result : $default); diff --git a/src/Connection/HttpResponse.php b/src/Connection/HttpResponse.php index 9f0fe69..fad35ab 100644 --- a/src/Connection/HttpResponse.php +++ b/src/Connection/HttpResponse.php @@ -47,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); diff --git a/src/Controller/Action.php b/src/Controller/Action.php index fab01da..84cf844 100644 --- a/src/Controller/Action.php +++ b/src/Controller/Action.php @@ -231,7 +231,7 @@ class Action implements ActionInterface /** * Страница по умолчанию * @param HttpRequest $request - * @return View|string + * @return string|false */ public function actionIndex(HttpRequest $request) { return ""; diff --git a/src/Controller/Component.php b/src/Controller/Component.php index a6916a5..124d4e5 100644 --- a/src/Controller/Component.php +++ b/src/Controller/Component.php @@ -16,7 +16,7 @@ use ctiso\Controller\SiteInterface; use ctiso\Database\PDOStatement; use PHPTAL; use PHPTAL_PreFilter_Normalize; -use ctiso\View\FakeTemplate; +use ctiso\View\JsonView; /** * Класс компонента @@ -117,12 +117,12 @@ class Component /** * Получить шаблон * @param string $name - * @return PHPTAL|FakeTemplate + * @return PHPTAL|JsonView */ public function getView($name) { if ($this->output === 'json') { - return new FakeTemplate($name); + return new JsonView($name); } /** @var Registry $config */ @@ -135,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; diff --git a/src/Controller/Front.php b/src/Controller/Front.php index 1a64a65..b799f40 100644 --- a/src/Controller/Front.php +++ b/src/Controller/Front.php @@ -111,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/Service.php b/src/Controller/Service.php index 7e00a06..e4d7fb9 100644 --- a/src/Controller/Service.php +++ b/src/Controller/Service.php @@ -55,6 +55,7 @@ class Service */ public function getModel($modelName) { + /** @var BaseMapper */ $model = new $modelName(); $model->db = $this->db; return $model; diff --git a/src/Database.php b/src/Database.php index a15b787..ab2a233 100644 --- a/src/Database.php +++ b/src/Database.php @@ -53,6 +53,12 @@ namespace ctiso { 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 diff --git a/src/Database/Manager.php b/src/Database/Manager.php index 19ed189..ff8211e 100644 --- a/src/Database/Manager.php +++ b/src/Database/Manager.php @@ -16,7 +16,7 @@ use Exception; * @phpstan-type CreateAction array{ * type:"createTable", * table_name:string, - * constraints:?array, + * constraints?:array{fields: array, type: string}|string, * fields:array, * } * @@ -277,7 +277,7 @@ class Manager * Добавляет столбец в таблицу * @param string $table_name * @param string $column_name - * @param array $field + * @param ColumnProps $field */ public function addColumn($table_name, $column_name, $field): void { diff --git a/src/Filter/Login.php b/src/Filter/Login.php index 93df732..50475ee 100644 --- a/src/Filter/Login.php +++ b/src/Filter/Login.php @@ -41,7 +41,7 @@ class Login extends Filter /** * Проверка авторизации * @param HttpRequest $request - * @return Boolean Авторизовани пользователь или нет + * @return bool Авторизовани пользователь или нет */ public function isLoggin(HttpRequest $request) { @@ -50,8 +50,8 @@ class Login extends Filter switch ($request->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) { @@ -81,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, для проверки пароля @@ -154,7 +154,7 @@ class Login extends Filter 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'); @@ -166,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 { @@ -177,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"]); } @@ -187,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']); } } diff --git a/src/Form/Select.php b/src/Form/Select.php index 333691a..89ea70d 100644 --- a/src/Form/Select.php +++ b/src/Form/Select.php @@ -4,7 +4,7 @@ namespace ctiso\Form; use ctiso\Form\Field; /** - * @phpstan-type Option = array{value: string, name: string, selected: bool, class?: string|false} + * @phpstan-type Option = array{value: string, name: string, selected?: bool, class?: string|false} */ class Select extends Field { diff --git a/src/Functions.php b/src/Functions.php index 1d7dd87..bd7fead 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -268,7 +268,7 @@ class Functions { /** * @param string $key - * @param list|\ArrayIterator $array + * @param array|\ArrayIterator $array * @return array */ static function key_values_object($key, $array) { @@ -283,7 +283,7 @@ class Functions { /** * @param string $key * @param string $value - * @param list>|\ArrayIterator $array + * @param array>|\ArrayIterator $array * @return array */ static function assoc_key_values($key, $value, $array) { @@ -296,7 +296,7 @@ class Functions { /** * @param string $key - * @param list>|\ArrayIterator $array + * @param array>|\ArrayIterator $array * @return array */ static function assoc_key($key, $array) { diff --git a/src/HttpRequest.php b/src/HttpRequest.php index da5c547..a24e60f 100644 --- a/src/HttpRequest.php +++ b/src/HttpRequest.php @@ -5,8 +5,7 @@ */ namespace ctiso; -use Exception; -use ArrayAccess; + use ctiso\Collection; use ctiso\Session; @@ -55,7 +54,7 @@ class HttpRequest extends Collection /** * @param string $key * @param mixed $default - * @return mixed + * @return null|string|array */ function get($key, $default = null) { @@ -67,6 +66,26 @@ class HttpRequest extends Collection 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) { 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/Path.php b/src/Path.php index b9b386a..55a0022 100644 --- a/src/Path.php +++ b/src/Path.php @@ -21,7 +21,7 @@ class Path */ public function __construct($path = '') { - $this->url = parse_url($path); + $this->url = parse_url($path) ?: []; if (isset($this->url['path'])) { $path = $this->url['path']; @@ -60,7 +60,7 @@ class Path */ public static function basename($path) { - $list = preg_split('#\\\\|/#s', $path); + $list = preg_split('#\\\\|/#s', $path) ?: ['']; return end($list); } @@ -96,7 +96,7 @@ class Path static function skipExtension(string $fileName): string { $path = pathinfo($fileName); - if ($path['dirname'] === ".") { + if (!isset($path['dirname']) || $path['dirname'] === ".") { return $path['filename']; } else { return self::join($path['dirname'], $path['filename']); @@ -120,12 +120,12 @@ class Path * Преобразует строку пути в массив * * @param string $path Путь - * @return list|false + * @return list */ - public static function listFromString(string $path): array|false + public static function listFromString(string $path): array { $list = preg_split('#\\\\|/#s', $path); - return $list; + return $list ?: []; } /** @@ -174,17 +174,17 @@ class 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; } /** diff --git a/src/Role/User.php b/src/Role/User.php index fc96b8d..78f2923 100644 --- a/src/Role/User.php +++ b/src/Role/User.php @@ -74,7 +74,9 @@ class User implements UserInterface 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; } diff --git a/src/Session.php b/src/Session.php index 6085450..ff2bba6 100644 --- a/src/Session.php +++ b/src/Session.php @@ -16,10 +16,7 @@ class Session { if (is_array($key)) { $className = get_class($key[0]); - /*if ($className === false) { - throw new \RuntimeException("Invalid class name " . $className); - }*/ - $_SESSION[strtolower($className)][$key[1]] = $value; + $_SESSION[strtolower($className ?: '')][$key[1]] = $value; } else { $_SESSION[$key] = $value; } diff --git a/src/Setup.php b/src/Setup.php index 09a428f..e3538be 100644 --- a/src/Setup.php +++ b/src/Setup.php @@ -10,6 +10,7 @@ namespace ctiso; use ctiso\Tools\SQLStatementExtractor; use ctiso\Path; +use ctiso\File; use SimpleXMLElement; class FakeZipArchive { @@ -26,7 +27,7 @@ class FakeZipArchive { * @return string */ function getFromName($file) { - return file_get_contents(Path::join($this->base, $file)); + return File::getContents(Path::join($this->base, $file)); } } diff --git a/src/Tools/StringUtil.php b/src/Tools/StringUtil.php index aba87c3..a5a37ce 100644 --- a/src/Tools/StringUtil.php +++ b/src/Tools/StringUtil.php @@ -96,11 +96,11 @@ class StringUtil /** * Разбивает строку на массив символов * @param string $str - * @return array|false + * @return array */ - static function mb_str_split(string $str): array|false + static function mb_str_split(string $str): array { - return preg_split('~~u', $str, -1, PREG_SPLIT_NO_EMPTY); + return preg_split('~~u', $str, -1, PREG_SPLIT_NO_EMPTY) ?: []; } /** diff --git a/src/Tools/TemplateImage.php b/src/Tools/TemplateImage.php index 690a4a4..cb0ad0d 100644 --- a/src/Tools/TemplateImage.php +++ b/src/Tools/TemplateImage.php @@ -196,12 +196,16 @@ class TemplateImage return $text; } - function setSize(int $new_width, int $new_height): void + /** + * @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 diff --git a/src/Validator/Rule/AbstractRule.php b/src/Validator/Rule/AbstractRule.php index fac4aad..706d8b6 100644 --- a/src/Validator/Rule/AbstractRule.php +++ b/src/Validator/Rule/AbstractRule.php @@ -1,13 +1,14 @@ 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 7576ca8..2225479 100644 --- a/src/Validator/Validator.php +++ b/src/Validator/Validator.php @@ -4,15 +4,16 @@ * Проверка коллекции */ 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, // Описание правила см. формат правила ниже + * validate?:string, // Описание правила см. формат правила ниже * name:string, // Имя переменой для проверки - * context?:object + * context?:RuleContext * } */ class Validator diff --git a/src/View/FakeTemplate.php b/src/View/JsonView.php similarity index 73% rename from src/View/FakeTemplate.php rename to src/View/JsonView.php index 223fa46..3ba8fd4 100644 --- a/src/View/FakeTemplate.php +++ b/src/View/JsonView.php @@ -2,7 +2,7 @@ namespace ctiso\View; -class FakeTemplate extends \stdClass { +class JsonView extends \stdClass { /** @var array */ public $_data = []; /** @var string */ @@ -15,6 +15,14 @@ class FakeTemplate extends \stdClass { $this->_name = $name; } + /** + * @param string $key + * @param mixed $value + */ + function set($key, $value): void { + $this->_data[$key] = $value; + } + /** * @param string $key * @param mixed $value