Files
ave-cms/system/App/Common/SettingsRegistry.php
T
2026-07-27 12:58:44 +03:00

256 lines
7.0 KiB
PHP

<?php
/*
|--------------------------------------------------------------------------------------
| AVE.cms
|--------------------------------------------------------------------------------------
| @package AVE.cms
| @file system/App/Common/SettingsRegistry.php
| @author AVE.cms <support@ave-cms.ru>
| @copyright 2007-2026 (c) AVE.cms
| @link https://ave-cms.ru
| @version 3.3
*/
namespace App\Common;
defined('BASEPATH') || die('Direct access to this location is not allowed.');
/**
* Реестр описаний настроек.
*
* Settings хранит key/value. SettingsRegistry хранит metadata для UI,
* defaults, validation и безопасного сохранения typed values.
*/
class SettingsRegistry
{
protected static $items = array();
public static function add($key, array $definition = array(), $module = null)
{
$definition['key'] = self::fullKey($key, $module);
if ($module !== null && !isset($definition['module'])) {
$definition['module'] = (string) $module;
}
$definition = self::normalize($definition);
self::$items[$definition['key']] = $definition;
}
public static function addMany(array $definitions, $module = null)
{
foreach ($definitions as $key => $definition) {
if (!is_array($definition)) {
continue;
}
if (is_int($key)) {
if (empty($definition['key'])) {
continue;
}
self::add($definition['key'], $definition, isset($definition['module']) ? $definition['module'] : $module);
} else {
self::add($key, $definition, isset($definition['module']) ? $definition['module'] : $module);
}
}
}
public static function has($key)
{
return isset(self::$items[(string) $key]);
}
public static function get($key)
{
$key = (string) $key;
return isset(self::$items[$key]) ? self::$items[$key] : null;
}
public static function all($group = null)
{
$items = array_values(self::$items);
if ($group !== null && $group !== '') {
$group = (string) $group;
$items = array_values(array_filter($items, function ($item) use ($group) {
return isset($item['group']) && $item['group'] === $group;
}));
}
usort($items, function ($a, $b) {
$sa = isset($a['sort_order']) ? (int) $a['sort_order'] : 100;
$sb = isset($b['sort_order']) ? (int) $b['sort_order'] : 100;
if ($sa === $sb) {
return strcmp((string) $a['key'], (string) $b['key']);
}
return $sa < $sb ? -1 : 1;
});
return $items;
}
public static function groups()
{
$groups = array();
foreach (self::$items as $item) {
$group = isset($item['group']) ? (string) $item['group'] : 'general';
if (!isset($groups[$group])) {
$groups[$group] = array(
'code' => $group,
'label' => isset($item['group_label']) ? (string) $item['group_label'] : $group,
'count' => 0,
);
}
$groups[$group]['count']++;
}
return array_values($groups);
}
public static function defaultValue($key, $fallback = null)
{
$item = self::get($key);
if (!$item || !array_key_exists('default', $item)) {
return $fallback;
}
return $item['default'];
}
public static function value($key, $fallback = null)
{
return Settings::get($key, self::defaultValue($key, $fallback));
}
public static function save($key, $value, $actorId = null)
{
$item = self::get($key);
if (!$item) {
throw new \InvalidArgumentException('Unknown setting: ' . (string) $key);
}
$result = self::validate($item, $value);
if (!$result['success']) {
throw new \InvalidArgumentException($result['message']);
}
Settings::set($item['key'], $result['value'], $item['type']);
AuditLog::record('settings.updated', array(
'actor_id' => $actorId ? (int) $actorId : null,
'target_type' => 'setting',
'meta' => array(
'key' => $item['key'],
'module' => $item['module'],
'type' => $item['type'],
),
));
return $result['value'];
}
public static function validate(array $item, $value)
{
$type = isset($item['type']) ? (string) $item['type'] : 'string';
$rules = isset($item['validation']) && is_array($item['validation']) ? $item['validation'] : array();
if (!empty($rules['required']) && ($value === null || $value === '')) {
return array('success' => false, 'message' => 'Значение обязательно');
}
$value = self::cast($value, $type);
if (($type === 'int' || $type === 'float') && $value !== null && $value !== '') {
if (isset($rules['min']) && $value < $rules['min']) {
return array('success' => false, 'message' => 'Значение меньше минимума');
}
if (isset($rules['max']) && $value > $rules['max']) {
return array('success' => false, 'message' => 'Значение больше максимума');
}
}
if (!empty($item['options']) && is_array($item['options'])) {
$allowed = array_keys($item['options']);
if (array_values($item['options']) === $item['options']) {
$allowed = $item['options'];
}
if (!in_array($value, $allowed, true)) {
return array('success' => false, 'message' => 'Недопустимое значение');
}
}
return array('success' => true, 'value' => $value, 'message' => '');
}
protected static function normalize(array $definition)
{
$definition = array_merge(array(
'key' => '',
'label' => '',
'type' => 'string',
'default' => '',
'group' => 'general',
'group_label' => '',
'module' => '',
'description' => '',
'options' => array(),
'validation' => array(),
'visibility' => 'public',
'sensitive' => false,
'sort_order' => 100,
), $definition);
$definition['key'] = trim((string) $definition['key']);
$definition['type'] = self::normalizeType($definition['type']);
$definition['default'] = self::cast($definition['default'], $definition['type']);
$definition['sensitive'] = !empty($definition['sensitive']);
return $definition;
}
protected static function fullKey($key, $module = null)
{
$key = trim((string) $key);
$module = trim((string) $module);
if ($module !== '' && strpos($key, $module . '.') !== 0) {
return $module . '.' . $key;
}
return $key;
}
protected static function normalizeType($type)
{
$type = strtolower(trim((string) $type));
$allowed = array('string', 'text', 'bool', 'int', 'float', 'select', 'json', 'code', 'html', 'image', 'file');
return in_array($type, $allowed, true) ? $type : 'string';
}
protected static function cast($value, $type)
{
switch ($type) {
case 'bool':
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) === true;
case 'int':
return (int) $value;
case 'float':
return (float) $value;
case 'json':
if (is_array($value)) {
return $value;
}
$data = json_decode((string) $value, true);
return is_array($data) ? $data : array();
default:
return (string) $value;
}
}
}