Use parse_ini_file instead of custom class.

This commit is contained in:
flash 2018-09-16 01:27:12 +02:00
parent c8aafe1a07
commit bce19a2ed6
11 changed files with 161 additions and 477 deletions

View file

@ -223,7 +223,7 @@ MIG;
tpl_add_path(__DIR__ . '/templates');
if ($app->getConfig()->get('Auth', 'lockdown', 'bool', false)) {
if ($app->underLockdown()) {
http_response_code(503);
echo tpl_render('auth/lockdown');
exit;

View file

@ -7,8 +7,6 @@ use Misuzu\Users\Session;
require_once __DIR__ . '/../misuzu.php';
$config = $app->getConfig();
$usernameValidationErrors = [
'trim' => 'Your username may not start or end with spaces!',
'short' => sprintf('Your username is too short, it has to be at least %d characters!', MSZ_USERNAME_MIN_LENGTH),
@ -18,7 +16,7 @@ $usernameValidationErrors = [
];
$authMode = $_GET['m'] ?? 'login';
$preventRegistration = $config->get('Auth', 'prevent_registration', 'bool', false);
$preventRegistration = $app->disableRegistration();
tpl_vars([
'prevent_registration' => $preventRegistration,
@ -203,10 +201,7 @@ If you weren't the person who requested this reset, please send a reply to this
MSG;
$message = (new Swift_Message('Flashii Password Reset'))
->setFrom([
$config->get('Mail', 'sender_email', 'string', 'sys@misuzu.lh') =>
$config->get('Mail', 'sender_name', 'string', 'Misuzu')
])
->setFrom($app->getMailSender())
->setTo([$forgotUser['email'] => $forgotUser['username']])
->setBody($messageBody);

View file

@ -5,17 +5,7 @@ use Misuzu\Database;
require_once __DIR__ . '/../misuzu.php';
$config = $app->getConfig();
if ($config->get('Site', 'embed_linked_data', 'bool', false)) {
tpl_vars([
'embed_linked_data' => true,
'embed_name' => $config->get('Site', 'name'),
'embed_url' => $config->get('Site', 'url'),
'embed_logo' => $config->get('Site', 'external_logo'),
'embed_same_as' => explode(',', $config->get('Site', 'social_media')),
]);
}
tpl_vars($app->getLinkedData());
$news = Database::query('
SELECT

View file

@ -9,10 +9,7 @@ $mode = (string)($_GET['m'] ?? 'view');
switch ($mode) {
case 'avatar':
$avatar_filename = $app->getPath(
$app->getConfig()->get('Avatar', 'default_path', 'string', 'public/images/no-avatar.png')
);
$avatar_filename = $app->getDefaultAvatar();
$user_avatar = "{$user_id}.msz";
$cropped_avatar = $app->getStore('avatars/200x200')->filename($user_avatar);

View file

@ -66,11 +66,9 @@ if (!array_key_exists($settingsMode, $settingsModes)) {
$settingsErrors = [];
$disableAccountOptions = !$app->inDebugMode() && $app->getConfig()->get('Auth', 'prevent_registration', 'bool', false);
$disableAccountOptions = !$app->inDebugMode() && $app->disableRegistration();
$avatarFileName = "{$app->getUserId()}.msz";
$avatarWidthMax = $app->getConfig()->get('Avatar', 'max_width', 'int', 4000);
$avatarHeightMax = $app->getConfig()->get('Avatar', 'max_height', 'int', 4000);
$avatarFileSizeMax = $app->getConfig()->get('Avatar', 'max_filesize', 'int', 1000000);
$avatarProps = $app->getAvatarProps();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
@ -123,9 +121,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$avatarErrorStrings['upload'][$_FILES['avatar']['error']['file']]
?? $avatarErrorStrings['upload']['default'],
$_FILES['avatar']['error']['file'],
byte_symbol($avatarFileSizeMax, true),
$avatarWidthMax,
$avatarHeightMax
byte_symbol($avatarProps['max_filesize'], true),
$avatarProps['max_width'],
$avatarProps['max_height']
);
break;
}
@ -140,9 +138,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$avatarErrorStrings['set'][$setAvatar]
?? $avatarErrorStrings['set']['default'],
$setAvatar,
byte_symbol($avatarFileSizeMax, true),
$avatarWidthMax,
$avatarHeightMax
byte_symbol($avatarProps['max_filesize'], true),
$avatarProps['max_width'],
$avatarProps['max_height']
);
}
break;
@ -309,9 +307,9 @@ switch ($settingsMode) {
tpl_vars([
'avatar_user_id' => $app->getUserId(),
'avatar_max_width' => $avatarWidthMax,
'avatar_max_height' => $avatarHeightMax,
'avatar_max_filesize' => $avatarFileSizeMax,
'avatar_max_width' => $avatarProps['max_width'],
'avatar_max_height' => $avatarProps['max_height'],
'avatar_max_filesize' => $avatarProps['max_filesize'],
'user_has_avatar' => $userHasAvatar,
'settings_profile_fields' => $profileFields,
'settings_profile_values' => $userFields,

View file

@ -2,7 +2,6 @@
namespace Misuzu;
use Carbon\Carbon;
use Misuzu\Config\ConfigManager;
use Misuzu\IO\Directory;
use Misuzu\IO\DirectoryDoesNotExistException;
use Misuzu\Users\Session;
@ -12,6 +11,7 @@ use Swift_Mailer;
use Swift_NullTransport;
use Swift_SmtpTransport;
use Swift_SendmailTransport;
use GeoIp2\Database\Reader as GeoIP;
/**
* Handles the set up procedures.
@ -50,11 +50,7 @@ class Application extends ApplicationBase
*/
private $currentUserId = 0;
/**
* ConfigManager instance.
* @var \Misuzu\Config\ConfigManager
*/
private $configInstance = null;
private $config = [];
/**
* TemplatingEngine instance.
@ -64,6 +60,8 @@ class Application extends ApplicationBase
private $mailerInstance = null;
private $geoipInstance = null;
private $startupTime = 0;
/**
@ -76,14 +74,18 @@ class Application extends ApplicationBase
$this->startupTime = microtime(true);
parent::__construct();
$this->debugMode = $debug;
$this->configInstance = new ConfigManager($configFile);
$this->config = parse_ini_file($configFile, true, INI_SCANNER_TYPED);
if ($this->config === false) {
throw new UnexpectedValueException('Failed to parse configuration.');
}
// only use this error handler in prod mode, dev uses Whoops now
if (!$debug) {
ExceptionHandler::register(
false,
$this->configInstance->get('Exceptions', 'report_url', 'string', null),
$this->configInstance->get('Exceptions', 'hash_key', 'string', null)
$this->config['Exceptions']['report_url'] ?? null,
$this->config['Exceptions']['hash_key'] ?? null
);
}
}
@ -93,21 +95,6 @@ class Application extends ApplicationBase
return microtime(true) - $this->startupTime;
}
/**
* Gets instance of the config manager.
* @return ConfigManager
*/
public function getConfig(): ConfigManager
{
if (is_null($this->configInstance)) {
throw new UnexpectedValueException(
'Internal ConfigManager instance is null, how did you even manage to do this?'
);
}
return $this->configInstance;
}
/**
* Gets whether we're in debug mode or not.
* @return bool
@ -143,7 +130,7 @@ class Application extends ApplicationBase
if (starts_with($append, '/')) {
$path = $append;
} else {
$path = $this->getConfig()->get('Storage', 'path', 'string', __DIR__ . '/../store');
$path = $this->config['Storage']['path'] ?? __DIR__ . '/../store';
if (!empty($append)) {
$path .= '/' . $append;
@ -164,9 +151,9 @@ class Application extends ApplicationBase
{
$override_key = 'override_' . str_replace('/', '_', $purpose);
if ($this->configInstance->contains('Storage', $override_key)) {
if (array_key_exists('Storage', $this->config)) {
try {
return new Directory($this->configInstance->get('Storage', $override_key));
return new Directory($this->config['Storage'][$override_key]);
} catch (DirectoryDoesNotExistException $ex) {
// fall through and just get the default path.
}
@ -235,7 +222,13 @@ class Application extends ApplicationBase
throw new UnexpectedValueException('Database has already been started.');
}
new Database($this->configInstance, self::DATABASE_CONNECTIONS[0]);
$connections = [];
foreach (self::DATABASE_CONNECTIONS as $name) {
$connections[$name] = $this->config["Database.{$name}"] ?? [];
}
new Database($connections, self::DATABASE_CONNECTIONS[0]);
}
/**
@ -248,11 +241,11 @@ class Application extends ApplicationBase
}
new Cache(
$this->configInstance->get('Cache', 'host', 'string', null),
$this->configInstance->get('Cache', 'port', 'int', null),
$this->configInstance->get('Cache', 'database', 'int', null),
$this->configInstance->get('Cache', 'password', 'string', null),
$this->configInstance->get('Cache', 'prefix', 'string', '')
$this->config['Cache']['host'] ?? null,
$this->config['Cache']['port'] ?? null,
$this->config['Cache']['database'] ?? null,
$this->config['Cache']['password'] ?? null,
$this->config['Cache']['prefix'] ?? ''
);
}
@ -270,10 +263,10 @@ class Application extends ApplicationBase
]);
tpl_var('globals', [
'site_name' => $this->configInstance->get('Site', 'name', 'string', 'Flashii'),
'site_description' => $this->configInstance->get('Site', 'description'),
'site_twitter' => $this->configInstance->get('Site', 'twitter'),
'site_url' => $this->configInstance->get('Site', 'url'),
'site_name' => $this->config['Site']['name'] ?? 'Flashii',
'site_description' => $this->config['Site']['description'] ?? '',
'site_twitter' => $this->config['Site']['twitter'] ?? '',
'site_url' => $this->config['Site']['url'] ?? '',
]);
tpl_add_function('json_decode', true);
@ -309,8 +302,8 @@ class Application extends ApplicationBase
return;
}
if ($this->configInstance->contains('Mail')) {
$method = mb_strtolower($this->configInstance->get('Mail', 'method'));
if (array_key_exists('Mail', $this->config) && array_key_exists('method', $this->config['Mail'])) {
$method = mb_strtolower($this->config['Mail']['method'] ?? '');
}
if (empty($method) || !array_key_exists($method, self::MAIL_TRANSPORT)) {
@ -322,27 +315,25 @@ class Application extends ApplicationBase
switch ($method) {
case 'sendmail':
if ($this->configInstance->contains('Mail', 'command')) {
$transport->setCommand(
$this->configInstance->get('Mail', 'command')
);
if (array_key_exists('command', $this->config['Mail'])) {
$transport->setCommand($this->config['Mail']['command']);
}
break;
case 'smtp':
$transport->setHost($this->configInstance->get('Mail', 'host'));
$transport->setPort($this->configInstance->get('Mail', 'port', 'int', 25));
$transport->setHost($this->config['Mail']['host'] ?? '');
$transport->setPort(intval($this->config['Mail']['port'] ?? 25));
if ($this->configInstance->contains('Mail', 'encryption')) {
$transport->setEncryption($this->configInstance->get('Mail', 'encryption'));
if (array_key_exists('encryption', $this->config['Mail'])) {
$transport->setEncryption($this->config['Mail']['encryption']);
}
if ($this->configInstance->contains('Mail', 'username')) {
$transport->setUsername($this->configInstance->get('Mail', 'username'));
if (array_key_exists('username', $this->config['Mail'])) {
$transport->setUsername($this->config['Mail']['username']);
}
if ($this->configInstance->contains('Mail', 'password')) {
$transport->setPassword($this->configInstance->get('Mail', 'password'));
if (array_key_exists('password', $this->config['Mail'])) {
$transport->setPassword($this->config['Mail']['password']);
}
break;
}
@ -363,4 +354,73 @@ class Application extends ApplicationBase
{
return self::getInstance()->getMailer();
}
public function getMailSender(): array
{
return [
($this->config['Mail']['sender_email'] ?? 'sys@msz.lh') => ($this->config['Mail']['sender_name'] ?? 'Misuzu System')
];
}
public function startGeoIP(): void
{
if (!empty($this->geoipInstance)) {
return;
}
$this->geoipInstance = new GeoIP($this->config['GeoIP']['database_path'] ?? '');
}
public function getGeoIP(): GeoIP
{
if (empty($this->geoipInstance)) {
$this->startGeoIP();
}
return $this->geoipInstance;
}
public static function geoip(): GeoIP
{
return self::getInstance()->getGeoIP();
}
public function getAvatarProps(): array
{
return [
'max_width' => intval($this->config['Avatar']['max_width'] ?? 4000),
'max_height' => intval($this->config['Avatar']['max_height'] ?? 4000),
'max_filesize' => intval($this->config['Avatar']['max_filesize'] ?? 1000000),
];
}
public function underLockdown(): bool
{
return boolval($this->config['Auth']['lockdown'] ?? false);
}
public function disableRegistration(): bool
{
return $this->underLockdown() || boolval($this->config['Auth']['prevent_registration'] ?? false);
}
public function getLinkedData(): array
{
if (!($this->config['Site']['embed_linked_data'] ?? false)) {
return ['embed_linked_data' => false];
}
return [
'embed_linked_data' => true,
'embed_name' => $this->config['Site']['name'] ?? 'Flashii',
'embed_url' => $this->config['Site']['url'] ?? '',
'embed_logo' => $this->config['Site']['external_logo'] ?? '',
'embed_same_as' => explode(',', $this->config['Site']['social_media'] ?? '')
];
}
public function getDefaultAvatar(): string
{
return $this->getPath($this->config['Avatar']['default_path'] ?? 'public/images/no-avatar.png');
}
}

View file

@ -1,7 +1,6 @@
<?php
namespace Misuzu;
use Misuzu\Config\ConfigManager;
use Redis;
use InvalidArgumentException;
use UnexpectedValueException;

View file

@ -1,249 +0,0 @@
<?php
namespace Misuzu\Config;
use Misuzu\IO\File;
use Misuzu\IO\FileStream;
/**
* Handles parsing, reading and setting configuration files.
* @package Aitemu\Config
* @author flashwave <me@flash.moe>
*/
class ConfigManager
{
/**
* Holds the key collection pairs for the sections.
* @var array
*/
private $collection = [];
private $filename = null;
/**
* Creates a file object with the given path and reloads the context.
* @param string|null $filename
*/
public function __construct(?string $filename = null)
{
if (empty($filename)) {
return;
}
$this->filename = $filename;
if (File::exists($this->filename)) {
$this->load();
}
}
/**
* Checks if a section or key exists in the config.
* @param string $section
* @param string $key
* @return bool
*/
public function contains(string $section, ?string $key = null): bool
{
if ($key !== null) {
return $this->contains($section) && array_key_exists($key, $this->collection[$section]);
}
return array_key_exists($section, $this->collection);
}
/**
* Removes a section or key and saves.
* @param string $section
* @param string $key
*/
public function remove(string $section, ?string $key = null): void
{
if ($key !== null && $this->contains($section, $key)) {
if (count($this->collection[$section]) < 2) {
$this->remove($section);
return;
}
unset($this->collection[$section][$key]);
} elseif ($this->contains($section)) {
unset($this->collection[$section]);
}
}
/**
* Gets a value from a section in the config.
* @param string $section
* @param string $key
* @param string $type
* @param string $fallback
* @return mixed
*/
public function get(string $section, string $key, string $type = 'string', ?string $fallback = null)
{
$value = null;
if (!$this->contains($section, $key)) {
$this->set($section, $key, $fallback);
return $fallback;
}
$raw = $this->collection[$section][$key];
switch (strtolower($type)) {
case "bool":
case "boolean":
$value = mb_strlen($raw) > 0 && ($raw[0] === '1' || mb_strtolower($raw) === "true");
break;
case "int":
case "integer":
$value = intval($raw);
break;
case "float":
case "double":
$value = floatval($raw);
break;
default:
$value = $raw;
break;
}
return $value;
}
/**
* Sets a configuration value and immediately saves it.
* @param string $section
* @param string $key
* @param mixed $value
*/
public function set(string $section, string $key, $value): void
{
$type = gettype($value);
$store = null;
switch (mb_strtolower($type)) {
case 'boolean':
$store = $value ? '1' : '0';
break;
default:
$store = (string)$value;
break;
}
if (!$this->contains($section)) {
$this->collection[$section] = [];
}
$this->collection[$section][$key] = $store;
}
/**
* Writes the serialised config to file.
*/
public function save(): void
{
if (!empty($this->filename)) {
static::write($this->filename, $this->collection);
}
}
/**
* Calls for a parse of the contents of the config file.
*/
public function load(): void
{
if (!empty($this->filename)) {
$this->collection = static::read($this->filename);
}
}
/**
* Serialises the $this->collection array to the human managable config format.
* @param string $filename
* @param array $collection
* @throws \Misuzu\IO\FileDoesNotExistException
* @throws \Misuzu\IO\IOException
*/
public static function write(string $filename, array $collection): void
{
$file = new FileStream($filename, FileStream::MODE_TRUNCATE, true);
$file->write(sprintf('; Saved on %s%s', date('Y-m-d H:i:s e'), PHP_EOL));
foreach ($collection as $name => $entries) {
if (count($entries) < 1) {
continue;
}
$file->write(sprintf('%1$s[%2$s]%1$s', PHP_EOL, $name));
foreach ($entries as $key => $value) {
$file->write(sprintf('%s = %s%s', $key, $value, PHP_EOL));
}
}
$file->flush();
$file->close();
}
/**
* Parses the config file.
* @param string $filename
* @return array
* @throws \Misuzu\IO\FileDoesNotExistException
* @throws \Misuzu\IO\IOException
*/
private static function read(string $filename): array
{
$collection = [];
$section = null;
$key = null;
$value = null;
$file = new FileStream($filename, FileStream::MODE_READ);
$lines = explode("\n", $file->read($file->length));
$file->close();
foreach ($lines as $line) {
$line = trim($line, "\r\n");
$length = mb_strlen($line);
if ($length < 1
|| starts_with($line, '#')
|| starts_with($line, ';')
|| starts_with($line, '//')
) {
continue;
}
if (starts_with($line, '[') && ends_with($line, ']')) {
$section = rtrim(ltrim($line, '['), ']');
if (!isset($collection[$section])) {
$collection[$section] = [];
}
continue;
}
if (mb_strpos($line, '=') !== false) {
$split = explode('=', $line, 2);
if (count($split) < 2) {
continue;
}
$key = trim($split[0]);
$value = trim($split[1]);
if (mb_strlen($key) > 0 && mb_strlen($value) > 0) {
$collection[$section][$key] = $value;
}
}
}
return $collection;
}
}

View file

@ -1,7 +1,6 @@
<?php
namespace Misuzu;
use Misuzu\Config\ConfigManager;
use PDO;
use PDOStatement;
use InvalidArgumentException;
@ -37,11 +36,6 @@ final class Database
*/
private $connections = [];
/**
* @var ConfigManager
*/
private $configManager;
/**
* @var string
*/
@ -62,7 +56,7 @@ final class Database
}
public function __construct(
ConfigManager $config,
array $connections,
string $default = 'default'
) {
if (self::hasInstance()) {
@ -71,7 +65,10 @@ final class Database
self::$instance = $this;
$this->default = $default;
$this->configManager = $config;
foreach ($connections as $name => $info) {
$this->addConnection($info, $name);
}
}
public static function connection(?string $name = null): PDO
@ -110,21 +107,17 @@ final class Database
return $this->connections[$name] ?? $this->addConnection($name);
}
public function addConnection(string $name): PDO
public function addConnection(array $info, string $name): PDO
{
$section = "Database.{$name}";
if (!$this->configManager->contains($section, 'driver')) {
if (!array_key_exists('driver', $info)) {
throw new InvalidArgumentException('Config section not found!');
}
$driver = $this->configManager->get($section, 'driver');
if (!in_array($driver, self::SUPPORTED)) {
if (!in_array($info['driver'], self::SUPPORTED)) {
throw new InvalidArgumentException('Unsupported driver.');
}
$dsn = $driver . ':';
$dsn = $info['driver'] . ':';
$options = [
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
@ -133,40 +126,29 @@ final class Database
PDO::ATTR_EMULATE_PREPARES => false,
];
switch ($driver) {
switch ($info['driver']) {
case 'sqlite':
if ($this->configManager->get($section, 'memory', 'bool', false)) {
if ($info['memory']) {
$dsn .= ':memory:';
} else {
$databasePath = realpath(
$this->configManager->get($section, 'database', 'string', __DIR__ . '/../store/misuzu.db')
);
$databasePath = realpath($info['database'] ?? __DIR__ . '/../store/misuzu.db');
if ($databasePath === false) {
throw new \UnexpectedValueException("Database does not exist.");
throw new UnexpectedValueException("Database does not exist.");
}
$dsn .= $databasePath . ';';
}
break;
case 'mysql':
$is_unix_socket = $this->configManager->contains($section, 'unix_socket');
if ($is_unix_socket) {
$dsn .= 'unix_socket=' . $this->configManager->get($section, 'unix_socket', 'string') . ';';
if ($info['unix_socket'] ?? false) {
$dsn .= 'unix_socket=' . $info['unix_socket'] . ';';
} else {
$dsn .= 'host=' . $this->configManager->get($section, 'host', 'string', self::DEFAULT_HOST) . ';';
$dsn .= 'port=' . $this->configManager->get($section, 'port', 'int', self::DEFAULT_PORT_MYSQL) . ';';
$dsn .= 'host=' . ($info['host'] ?? self::DEFAULT_HOST) . ';';
$dsn .= 'port=' . intval($info['port'] ?? self::DEFAULT_PORT_MYSQL) . ';';
}
$dsn .= 'charset=' . (
$this->configManager->contains($section, 'charset')
? $this->configManager->get($section, 'charset', 'string')
: 'utf8mb4'
) . ';';
$dsn .= 'dbname=' . $this->configManager->get($section, 'database', 'string', 'misuzu') . ';';
$dsn .= 'charset=' . ($info['charset'] ?? 'utf8mb4') . ';';
$dsn .= 'dbname=' . ($info['database'] ?? 'misuzu') . ';';
$options[PDO::MYSQL_ATTR_INIT_COMMAND] = "
SET SESSION
@ -178,8 +160,8 @@ final class Database
$connection = new PDO(
$dsn,
$this->configManager->get($section, 'username', 'string', null),
$this->configManager->get($section, 'password', 'string', null),
($info['username'] ?? null),
($info['password'] ?? null),
$options
);

View file

@ -1,67 +0,0 @@
<?php
namespace MisuzuTests;
use PHPUnit\Framework\TestCase;
use Misuzu\Config\ConfigManager;
define('CONFIG_FILE', sys_get_temp_dir() . '/MisuzuConfigTest' . time() . '.ini');
class ConfigTest extends TestCase
{
public function testMemoryConfigManager()
{
$config = new ConfigManager();
$this->assertInstanceOf(ConfigManager::class, $config);
$config->set('TestCat', 'string_val', 'test', 'string');
$config->set('TestCat', 'int_val', 25, 'int');
$config->set('TestCat', 'bool_val', true, 'bool');
$this->assertEquals('test', $config->get('TestCat', 'string_val', 'string'));
$this->assertEquals(25, $config->get('TestCat', 'int_val', 'int'));
$this->assertEquals(true, $config->get('TestCat', 'bool_val', 'bool'));
}
public function testConfigCreateSet()
{
$config = new ConfigManager(CONFIG_FILE);
$this->assertInstanceOf(ConfigManager::class, $config);
$config->set('TestCat', 'string_val', 'test', 'string');
$config->set('TestCat', 'int_val', 25, 'int');
$config->set('TestCat', 'bool_val', true, 'bool');
$this->assertEquals('test', $config->get('TestCat', 'string_val', 'string'));
$this->assertEquals(25, $config->get('TestCat', 'int_val', 'int'));
$this->assertEquals(true, $config->get('TestCat', 'bool_val', 'bool'));
$config->save();
}
public function testConfigReadGet()
{
$config = new ConfigManager(CONFIG_FILE);
$this->assertInstanceOf(ConfigManager::class, $config);
$this->assertEquals('test', $config->get('TestCat', 'string_val', 'string'));
$this->assertEquals(25, $config->get('TestCat', 'int_val', 'int'));
$this->assertEquals(true, $config->get('TestCat', 'bool_val', 'bool'));
}
public function testConfigRemove()
{
$config = new ConfigManager(CONFIG_FILE);
$this->assertInstanceOf(ConfigManager::class, $config);
$this->assertTrue($config->contains('TestCat', 'string_val'));
$config->remove('TestCat', 'string_val');
$config->save();
$config->load();
$this->assertFalse($config->contains('TestCat', 'string_val'));
// tack this onto here, deletes the entire file because we're done with it
\Misuzu\IO\File::delete(CONFIG_FILE);
}
}

View file

@ -1,12 +1,12 @@
<?php
function starts_with(string $string, string $text): bool
{
return substr($string, 0, strlen($text)) === $text;
return mb_substr($string, 0, mb_strlen($text)) === $text;
}
function ends_with(string $string, string $text): bool
{
return substr($string, 0 - strlen($text)) === $text;
return mb_substr($string, 0 - mb_strlen($text)) === $text;
}
function array_test(array $array, callable $func): bool
@ -40,7 +40,7 @@ function password_entropy(string $password): int
function check_mx_record(string $email): bool
{
$domain = substr(strstr($email, '@'), 1);
$domain = mb_substr(mb_strstr($email, '@'), 1);
return checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A');
}
@ -78,29 +78,8 @@ function byte_symbol($bytes, $decimal = false)
function get_country_code(string $ipAddr, string $fallback = 'XX'): string
{
global $_msz_geoip;
try {
if (!$_msz_geoip) {
$app = \Misuzu\Application::getInstance();
$config = $app->getConfig();
if ($config === null) {
return $fallback;
}
$database_path = $config->get('GeoIP', 'database_path');
if ($database_path === null) {
return $fallback;
}
$_msz_geoip = new \GeoIp2\Database\Reader($database_path);
}
$record = $_msz_geoip->country($ipAddr);
return $record->country->isoCode;
return \Misuzu\Application::geoip()->country($ipAddr)->country->isoCode ?? $fallback;
} catch (\Exception $e) {
// report error?
}
@ -186,7 +165,7 @@ function crop_image_centred(Imagick $image, int $target_width, int $target_heigh
function running_on_windows(): bool
{
return starts_with(strtolower(PHP_OS), 'win');
return starts_with(mb_strtolower(PHP_OS), 'win');
}
function first_paragraph(string $text, string $delimiter = "\n"): string
@ -227,7 +206,7 @@ function parse_bbcode(string $text): string
function is_local_url(string $url): bool
{
$length = strlen($url);
$length = mb_strlen($url);
if ($length < 1) {
return false;
@ -243,7 +222,7 @@ function is_local_url(string $url): bool
function parse_text(string $text, string $parser): string
{
switch (strtolower($parser)) {
switch (mb_strtolower($parser)) {
case 'md':
case 'markdown':
return \Misuzu\Parsers\MarkdownParser::instance()->parseText($text);
@ -259,7 +238,7 @@ function parse_text(string $text, string $parser): string
function parse_line(string $line, string $parser): string
{
switch (strtolower($parser)) {
switch (mb_strtolower($parser)) {
case 'md':
case 'markdown':
return \Misuzu\Parsers\MarkdownParser::instance()->parseLine($line);
@ -285,7 +264,7 @@ function render_info(?string $message, int $httpCode, string $template = 'errors
try {
tpl_var('http_code', $httpCode);
if (strlen($message)) {
if (mb_strlen($message)) {
tpl_var('message', $message);
}
@ -322,7 +301,7 @@ function html_link(string $url, ?string $content = null, $attributes = []): stri
['href' => $url]
);
if (strpos($url, '://') !== false) {
if (mb_strpos($url, '://') !== false) {
$attributes['target'] = '_blank';
$attributes['rel'] = 'noreferrer noopener';
}
@ -362,7 +341,7 @@ function url_construct(string $path, array $query = [], string $host = ''): stri
$url = $host . $path;
if (count($query)) {
$url .= strpos($path, '?') !== false ? '&' : '?';
$url .= mb_strpos($path, '?') !== false ? '&' : '?';
foreach ($query as $key => $value) {
if ($value) {
@ -371,12 +350,12 @@ function url_construct(string $path, array $query = [], string $host = ''): stri
}
}
return substr($url, 0, -1);
return mb_substr($url, 0, -1);
}
function camel_to_snake(string $camel): string
{
return trim(strtolower(preg_replace('#([A-Z][a-z]+)#', '$1_', $camel)), '_');
return trim(mb_strtolower(preg_replace('#([A-Z][a-z]+)#', '$1_', $camel)), '_');
}
function snake_to_camel(string $snake): string