mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
* try to give included files a logical structure * move objects from Util and Game to their own files * make non-essential files auto-loaded
452 lines
16 KiB
PHP
452 lines
16 KiB
PHP
<?php
|
|
|
|
namespace Aowow;
|
|
|
|
if (!defined('AOWOW_REVISION'))
|
|
die('illegal access');
|
|
|
|
class Cfg
|
|
{
|
|
public const PATTERN_CONF_KEY = '/[a-z0-9_\.\-]/i';
|
|
public const PATTERN_INV_CONF_KEY = '/[^a-z0-9_\.\-]/i';
|
|
public const PATTERN_INVALID_CHARS = '/\p{C}/ui';
|
|
|
|
// config flags
|
|
public const FLAG_TYPE_INT = 0x001; // validate with intVal()
|
|
public const FLAG_TYPE_FLOAT = 0x002; // validate with floatVal()
|
|
public const FLAG_TYPE_BOOL = 0x004; // 0 || 1
|
|
public const FLAG_TYPE_STRING = 0x008; //
|
|
public const FLAG_OPT_LIST = 0x010; // single option
|
|
public const FLAG_BITMASK = 0x020; // multiple options
|
|
public const FLAG_PHP = 0x040; // applied with ini_set() [restrictions apply!]
|
|
public const FLAG_PERSISTENT = 0x080; // can not be deleted
|
|
public const FLAG_REQUIRED = 0x100; // required to have non-empty value
|
|
public const FLAG_ON_LOAD_FN = 0x200; // run static function of the same name after load
|
|
public const FLAG_ON_SET_FN = 0x400; // run static function of the same name as validator
|
|
public const FLAG_INTERNAL = 0x800; // can not be configures, automaticly calculated, skip on lists
|
|
|
|
public const CAT_MISCELLANEOUS = 0;
|
|
public const CAT_SITE = 1;
|
|
public const CAT_CACHE = 2;
|
|
public const CAT_ACCOUNT = 3;
|
|
public const CAT_SESSION = 4;
|
|
public const CAT_SITE_REPUTATION = 5;
|
|
public const CAT_ANALYTICS = 6;
|
|
public const CAT_PROFILER = 7;
|
|
|
|
public static $categories = array( // don't mind the ordering ... please?
|
|
1 => 'Site', 'Caching', 'Account', 'Session', 'Site Reputation', 'Google Analytics', 'Profiler', 0 => 'Other'
|
|
);
|
|
|
|
private const IDX_VALUE = 0;
|
|
private const IDX_FLAGS = 1;
|
|
private const IDX_CATEGORY = 2;
|
|
private const IDX_DEFAULT = 3;
|
|
private const IDX_COMMENT = 4;
|
|
|
|
private static $store = []; // name => [value, flags, cat, default, comment]
|
|
private static $isLoaded = false;
|
|
|
|
private static $rebuildScripts = array(
|
|
// 'rep_req_border_unco' => ['global'], // currently not a template or buildScript
|
|
// 'rep_req_border_rare' => ['global'],
|
|
// 'rep_req_border_epic' => ['global'],
|
|
// 'rep_req_border_lege' => ['global'],
|
|
'profiler_enable' => ['realms', 'realmMenu'],
|
|
'battlegroup' => ['realms', 'realmMenu'],
|
|
'name_short' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'demo'],
|
|
'site_host' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'demo', 'power'],
|
|
'static_host' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'power'],
|
|
'contact_email' => ['markup'],
|
|
'locales' => ['locales']
|
|
);
|
|
|
|
public static function load() : void
|
|
{
|
|
if (!DB::isConnectable(DB_AOWOW))
|
|
return;
|
|
|
|
$sets = DB::Aowow()->select('SELECT `key` AS ARRAY_KEY, `value` AS "0", `flags` AS "1", `cat` AS "2", `default` AS "3", `comment` AS "4" FROM ?_config ORDER BY `key` ASC');
|
|
foreach ($sets as $key => [$value, $flags, $catg, $default, $comment])
|
|
{
|
|
$php = $flags & self::FLAG_PHP;
|
|
|
|
if ($err = self::validate($value, $flags, $comment))
|
|
{
|
|
self::throwError('Aowow config '.strtoupper($key).' failed validation and was skipped: '.$err);
|
|
continue;
|
|
}
|
|
|
|
if ($flags & self::FLAG_INTERNAL)
|
|
{
|
|
self::throwError('Aowow config '.strtoupper($key).' is flagged as internaly generated and should not have been set in DB.');
|
|
continue;
|
|
}
|
|
|
|
if ($flags & self::FLAG_ON_LOAD_FN)
|
|
{
|
|
if (!method_exists(__CLASS__, $key))
|
|
self::throwError('Aowow config '.strtoupper($key).' flagged for onLoadFN handling, but no handler was set');
|
|
else
|
|
self::{$key}($value);
|
|
}
|
|
|
|
if ($php)
|
|
ini_set(strtolower($key), $value);
|
|
|
|
self::$store[strtolower($key)] = [$value, $flags, $catg, $default, $comment];
|
|
}
|
|
|
|
if (CLI && !count(self::$store))
|
|
{
|
|
CLI::write('Cfg::load - aowow_config unexpectedly empty.', CLI::LOG_WARN);
|
|
return;
|
|
}
|
|
|
|
self::$isLoaded = true;
|
|
}
|
|
|
|
public static function add(string $key, /*int|string*/ $value) : string
|
|
{
|
|
if (!self::$isLoaded)
|
|
return 'used add() on uninitialized config';
|
|
|
|
if (!$key)
|
|
return 'empty option name given';
|
|
|
|
$key = strtolower($key);
|
|
|
|
if (preg_match(self::PATTERN_INV_CONF_KEY, $key))
|
|
return 'invalid chars in option name: [a-z 0-9 _ . -] are allowed';
|
|
|
|
if (isset(self::$store[$key]))
|
|
return 'this configuration option is already in use';
|
|
|
|
if ($errStr = self::validate($value))
|
|
return $errStr;
|
|
|
|
if (ini_get($key) === false || ini_set($key, $value) === false)
|
|
return 'this configuration option cannot be set';
|
|
|
|
$flags = self::FLAG_TYPE_STRING | self::FLAG_PHP;
|
|
if (!DB::Aowow()->query('INSERT IGNORE INTO ?_config (`key`, `value`, `cat`, `flags`) VALUES (?, ?, ?d, ?d)', $key, $value, self::CAT_MISCELLANEOUS, $flags))
|
|
return 'internal error';
|
|
|
|
self::$store[$key] = [$value, $flags, self::CAT_MISCELLANEOUS, null, null];
|
|
return '';
|
|
}
|
|
|
|
public static function delete(string $key) : string
|
|
{
|
|
if (!self::$isLoaded)
|
|
return 'used delete() on uninitialized config';
|
|
|
|
$key = strtolower($key);
|
|
|
|
if (!isset(self::$store[$key]))
|
|
return 'configuration option not found';
|
|
|
|
if (self::$store[$key][self::IDX_FLAGS] & self::FLAG_PERSISTENT)
|
|
return 'can\'t delete persistent option';
|
|
|
|
if (!(self::$store[$key][self::IDX_FLAGS] & self::FLAG_PHP))
|
|
return 'can\'t delete non-php option';
|
|
|
|
if (self::$store[$key][self::IDX_FLAGS] & self::FLAG_INTERNAL)
|
|
return 'can\'t delete internal option';
|
|
|
|
if (!DB::Aowow()->query('DELETE FROM ?_config WHERE `key` = ? AND (`flags` & ?d) = 0 AND (`flags` & ?d) > 0', $key, self::FLAG_PERSISTENT, self::FLAG_PHP))
|
|
return 'internal error';
|
|
|
|
unset(self::$store[$key]);
|
|
return '';
|
|
}
|
|
|
|
public static function get(string $key, bool $fromDB = false, bool $fullInfo = false) // : int|float|string
|
|
{
|
|
$key = strtolower($key);
|
|
|
|
if (!isset(self::$store[$key]))
|
|
{
|
|
if (self::$isLoaded)
|
|
self::throwError('cfg not defined: '.strtoupper($key));
|
|
|
|
return null;
|
|
}
|
|
|
|
if ($fromDB && $fullInfo)
|
|
return array_values(DB::Aowow()->selectRow('SELECT `value`, `flags`, `cat`, `default`, `comment` FROM ?_config WHERE `key` = ?', $key));
|
|
if ($fromDB)
|
|
return DB::Aowow()->selectCell('SELECT `value` FROM ?_config WHERE `key` = ?', $key);
|
|
if ($fullInfo)
|
|
return self::$store[$key];
|
|
|
|
return self::$store[$key][self::IDX_VALUE];
|
|
}
|
|
|
|
public static function set(string $key, /*int|string*/ $value, ?array &$rebuildFiles = []) : string
|
|
{
|
|
if (!self::$isLoaded)
|
|
return 'used set() on uninitialized config';
|
|
|
|
$key = strtolower($key);
|
|
|
|
if (!isset(self::$store[$key]))
|
|
return 'configuration option not found';
|
|
|
|
[$oldValue, $flags, , , $comment] = self::$store[$key];
|
|
|
|
if ($flags & self::FLAG_INTERNAL)
|
|
return 'can\'t set an internal option directly';
|
|
|
|
if ($err = self::validate($value, $flags, $comment))
|
|
return $err;
|
|
|
|
if ($flags & self::FLAG_REQUIRED && !strlen($value))
|
|
return 'empty value given for required config';
|
|
|
|
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $value, $key);
|
|
|
|
self::$store[$key][self::IDX_VALUE] = $value;
|
|
|
|
// validate change
|
|
if ($flags & self::FLAG_ON_SET_FN)
|
|
{
|
|
$errMsg = '';
|
|
if (!method_exists(__CLASS__, $key))
|
|
$errMsg = 'Aowow config '.strtoupper($key).' flagged for onSetFN validation, but no handler was set';
|
|
else
|
|
self::{$key}($value, $errMsg);
|
|
|
|
if ($errMsg)
|
|
{
|
|
// rollback change
|
|
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $oldValue, $key);
|
|
self::$store[$key][self::IDX_VALUE] = $oldValue;
|
|
|
|
return $errMsg;
|
|
}
|
|
}
|
|
|
|
if ($flags & self::FLAG_ON_LOAD_FN)
|
|
{
|
|
if (!method_exists(__CLASS__, $key))
|
|
return 'Aowow config '.strtoupper($key).' flagged for onLoadFN handling, but no handler was set';
|
|
else
|
|
self::{$key}($value);
|
|
}
|
|
|
|
// trigger setup build
|
|
return self::handleFileBuild($key, $rebuildFiles);
|
|
}
|
|
|
|
public static function reset(string $key, ?array &$rebuildFiles = []) : string
|
|
{
|
|
if (!self::$isLoaded)
|
|
return 'used reset() on uninitialized config';
|
|
|
|
$key = strtolower($key);
|
|
|
|
if (!isset(self::$store[$key]))
|
|
return 'configuration option not found';
|
|
|
|
[$oldValue, $flags, , $default, ] = self::$store[$key];
|
|
if ($flags & self::FLAG_INTERNAL)
|
|
return 'can\'t set an internal option directly';
|
|
|
|
if (!$default)
|
|
return 'config option has no default value';
|
|
|
|
// @eval .. some dafault values are supplied as bitmask or the likes
|
|
if (!($flags & Cfg::FLAG_TYPE_STRING))
|
|
$default = @eval('return ('.$default.');');
|
|
|
|
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $default, $key);
|
|
self::$store[$key][self::IDX_VALUE] = $default;
|
|
|
|
// validate change
|
|
if ($flags & self::FLAG_ON_SET_FN)
|
|
{
|
|
$errMsg = '';
|
|
if (!method_exists(__CLASS__, $key))
|
|
$errMsg = 'required onSetFN validator not set';
|
|
else
|
|
self::{$key}($default, $errMsg);
|
|
|
|
if ($errMsg)
|
|
{
|
|
// rollback change
|
|
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $oldValue, $key);
|
|
self::$store[$key][self::IDX_VALUE] = $oldValue;
|
|
|
|
return $errMsg;
|
|
}
|
|
}
|
|
|
|
// trigger setup build
|
|
return self::handleFileBuild($key, $rebuildFiles);
|
|
}
|
|
|
|
public static function forCategory(int $category) : \Generator
|
|
{
|
|
foreach (self::$store as $k => [, $flags, $catg, , ])
|
|
if ($catg == $category && !($flags & self::FLAG_INTERNAL))
|
|
yield $k => self::$store[$k];
|
|
}
|
|
|
|
public static function applyToString(string $string) : string
|
|
{
|
|
return preg_replace_callback(
|
|
['/CFG_([A-Z_]+)/', '/((HOST|STATIC)_URL)/'],
|
|
function ($m) {
|
|
if (!isset(self::$store[strtolower($m[1])]))
|
|
return $m[1];
|
|
|
|
[$val, $flags, , , ] = self::$store[strtolower($m[1])];
|
|
return $flags & (self::FLAG_TYPE_FLOAT | self::FLAG_TYPE_INT) ? Lang::nf($val) : $val;
|
|
},
|
|
$string
|
|
);
|
|
}
|
|
|
|
|
|
/************/
|
|
/* internal */
|
|
/************/
|
|
|
|
private static function validate(&$value, int $flags = self::FLAG_TYPE_STRING | self::FLAG_PHP, string $comment = ' - ') : string
|
|
{
|
|
$value = preg_replace(self::PATTERN_INVALID_CHARS, '', $value);
|
|
|
|
if (!($flags & (self::FLAG_TYPE_BOOL | self::FLAG_TYPE_FLOAT | self::FLAG_TYPE_INT | self::FLAG_TYPE_STRING)))
|
|
return 'no type set for value';
|
|
|
|
if ($flags & self::FLAG_TYPE_INT && !Util::checkNumeric($value, NUM_CAST_INT))
|
|
return 'value must be integer';
|
|
|
|
if ($flags & self::FLAG_TYPE_FLOAT && !Util::checkNumeric($value, NUM_CAST_FLOAT))
|
|
return 'value must be float';
|
|
|
|
if ($flags & self::FLAG_OPT_LIST)
|
|
{
|
|
$info = explode(' - ', $comment)[1];
|
|
foreach (explode(', ', $info) as $option)
|
|
if (explode(':', $option)[0] == $value)
|
|
return '';
|
|
|
|
return 'value not in range';
|
|
}
|
|
|
|
if ($flags & self::FLAG_BITMASK)
|
|
{
|
|
$mask = 0x0;
|
|
$info = explode(' - ', $comment)[1];
|
|
foreach (explode(', ', $info) as $option)
|
|
$mask |= (1 << explode(':', $option)[0]);
|
|
|
|
if (!($value &= $mask) && ($flags & self::FLAG_REQUIRED))
|
|
return 'value not in range';
|
|
}
|
|
|
|
if ($flags & self::FLAG_TYPE_BOOL)
|
|
$value = (bool)$value;
|
|
|
|
return '';
|
|
}
|
|
|
|
private static function handleFileBuild(string $key, array &$rebuildFiles) : string
|
|
{
|
|
if (!isset(self::$rebuildScripts[$key]))
|
|
return '';
|
|
|
|
$msg = '';
|
|
|
|
if (CLI)
|
|
{
|
|
$rebuildFiles = array_merge($rebuildFiles, self::$rebuildScripts[$key]);
|
|
return '';
|
|
}
|
|
|
|
// not in CLI mode and build() can only be run from CLI. .. todo: other options..?
|
|
exec('php aowow --build='.implode(',', self::$rebuildScripts[$key]), $out);
|
|
foreach ($out as $o)
|
|
if (strstr($o, 'ERR'))
|
|
$msg .= explode('0m]', $o)[1]."<br />\n";
|
|
|
|
return $msg;
|
|
}
|
|
|
|
private static function throwError($msg) : void
|
|
{
|
|
if (CLI)
|
|
CLI::write($msg, CLI::LOG_ERROR);
|
|
else
|
|
trigger_error($msg, E_USER_ERROR);
|
|
}
|
|
|
|
private static function locales(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
{
|
|
if (!CLI)
|
|
return true;
|
|
|
|
// note: Change is written to db and storage at this point, but can be rolled back.
|
|
if (CLISetup::setLocales())
|
|
return true;
|
|
|
|
$msg .= 'no valid locales set';
|
|
return false;
|
|
}
|
|
|
|
private static function acc_auth_mode(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
{
|
|
if ($value == 1 && !extension_loaded('gmp'))
|
|
{
|
|
$msg .= 'PHP extension GMP is required to use TrinityCore as auth source, but is not currently enabled.';
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static function profiler_enable(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
{
|
|
if ($value != 1)
|
|
return true;
|
|
|
|
return Profiler::queueStart($msg);
|
|
}
|
|
|
|
private static function static_host(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
{
|
|
self::$store['static_url'] = array( // points js to images & scripts
|
|
(self::useSSL() ? 'https://' : 'http://').$value,
|
|
self::FLAG_PERSISTENT | self::FLAG_TYPE_STRING | self::FLAG_INTERNAL,
|
|
self::CAT_SITE,
|
|
null, // no default value
|
|
null, // no comment/info
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
private static function site_host(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
{
|
|
self::$store['host_url'] = array( // points js to executable files
|
|
(self::useSSL() ? 'https://' : 'http://').$value,
|
|
self::FLAG_PERSISTENT | self::FLAG_TYPE_STRING | self::FLAG_INTERNAL,
|
|
self::CAT_SITE,
|
|
null, // no default value
|
|
null, // no comment/info
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
private static function useSSL() : bool
|
|
{
|
|
return (($_SERVER['HTTPS'] ?? 'off') != 'off') || (self::$store['force_ssl'][self::IDX_VALUE] ?? 0);
|
|
}
|
|
}
|
|
|
|
?>
|