Config: improve config loading and parsing error handling

closes #4654
closes #3454
This commit is contained in:
Dylan K. Taylor 2021-12-19 16:49:54 +00:00
parent 44e8603a6d
commit 65dabefa3b
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
5 changed files with 66 additions and 14 deletions

View File

@ -40,7 +40,7 @@
"pocketmine/callback-validator": "^1.0.2", "pocketmine/callback-validator": "^1.0.2",
"pocketmine/classloader": "^0.2.0", "pocketmine/classloader": "^0.2.0",
"pocketmine/color": "^0.2.0", "pocketmine/color": "^0.2.0",
"pocketmine/errorhandler": "^0.3.0", "pocketmine/errorhandler": "^0.4.0",
"pocketmine/locale-data": "^2.2.0", "pocketmine/locale-data": "^2.2.0",
"pocketmine/log": "^0.4.0", "pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0", "pocketmine/log-pthreads": "^0.4.0",

16
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "eff9aac3494dbd6798ed7bb62f962ae9", "content-hash": "f830ed22b7f602d1b03f52118371d559",
"packages": [ "packages": [
{ {
"name": "adhocore/json-comment", "name": "adhocore/json-comment",
@ -497,23 +497,23 @@
}, },
{ {
"name": "pocketmine/errorhandler", "name": "pocketmine/errorhandler",
"version": "0.3.0", "version": "0.4.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/pmmp/ErrorHandler.git", "url": "https://github.com/pmmp/ErrorHandler.git",
"reference": "ec742b209e8056bbe855069c4eff94c9734ea19b" "reference": "5bb9e2b66551ef7383da85165b7e8a7cc9aa162a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/ec742b209e8056bbe855069c4eff94c9734ea19b", "url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/5bb9e2b66551ef7383da85165b7e8a7cc9aa162a",
"reference": "ec742b209e8056bbe855069c4eff94c9734ea19b", "reference": "5bb9e2b66551ef7383da85165b7e8a7cc9aa162a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0" "php": "^7.2 || ^8.0"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "0.12.75", "phpstan/phpstan": "0.12.99",
"phpstan/phpstan-strict-rules": "^0.12.2" "phpstan/phpstan-strict-rules": "^0.12.2"
}, },
"type": "library", "type": "library",
@ -529,9 +529,9 @@
"description": "Utilities to handle nasty PHP E_* errors in a usable way", "description": "Utilities to handle nasty PHP E_* errors in a usable way",
"support": { "support": {
"issues": "https://github.com/pmmp/ErrorHandler/issues", "issues": "https://github.com/pmmp/ErrorHandler/issues",
"source": "https://github.com/pmmp/ErrorHandler/tree/0.3.0" "source": "https://github.com/pmmp/ErrorHandler/tree/0.4.0"
}, },
"time": "2021-02-12T18:56:22+00:00" "time": "2021-12-16T18:13:42+00:00"
}, },
{ {
"name": "pocketmine/locale-data", "name": "pocketmine/locale-data",

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\utils; namespace pocketmine\utils;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use Webmozart\PathUtil\Path; use Webmozart\PathUtil\Path;
use function array_change_key_case; use function array_change_key_case;
use function array_fill_keys; use function array_fill_keys;
@ -33,6 +34,7 @@ use function date;
use function explode; use function explode;
use function file_exists; use function file_exists;
use function file_get_contents; use function file_get_contents;
use function get_debug_type;
use function implode; use function implode;
use function is_array; use function is_array;
use function is_bool; use function is_bool;
@ -167,20 +169,31 @@ class Config{
if($content === false){ if($content === false){
throw new \RuntimeException("Unable to load config file"); throw new \RuntimeException("Unable to load config file");
} }
$config = null;
switch($this->type){ switch($this->type){
case Config::PROPERTIES: case Config::PROPERTIES:
$config = self::parseProperties($content); $config = self::parseProperties($content);
break; break;
case Config::JSON: case Config::JSON:
$config = json_decode($content, true); try{
$config = json_decode($content, true, flags: JSON_THROW_ON_ERROR);
}catch(\JsonException $e){
throw ConfigLoadException::wrap($this->file, $e);
}
break; break;
case Config::YAML: case Config::YAML:
$content = self::fixYAMLIndexes($content); $content = self::fixYAMLIndexes($content);
$config = yaml_parse($content); try{
$config = ErrorToExceptionHandler::trap(fn() => yaml_parse($content));
}catch(\ErrorException $e){
throw ConfigLoadException::wrap($this->file, $e);
}
break; break;
case Config::SERIALIZED: case Config::SERIALIZED:
$config = unserialize($content); try{
$config = ErrorToExceptionHandler::trap(fn() => unserialize($content));
}catch(\ErrorException $e){
throw ConfigLoadException::wrap($this->file, $e);
}
break; break;
case Config::ENUM: case Config::ENUM:
$config = array_fill_keys(self::parseList($content), true); $config = array_fill_keys(self::parseList($content), true);
@ -188,7 +201,10 @@ class Config{
default: default:
throw new \InvalidArgumentException("Invalid config type specified"); throw new \InvalidArgumentException("Invalid config type specified");
} }
$this->config = is_array($config) ? $config : $default; if(!is_array($config)){
throw new ConfigLoadException("Failed to load config $this->file: Expected array for base type, but got " . get_debug_type($config));
}
$this->config = $config;
if($this->fillDefaults($default, $this->config) > 0){ if($this->fillDefaults($default, $this->config) > 0){
$this->save(); $this->save();
} }

View File

@ -0,0 +1,31 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\utils;
final class ConfigLoadException extends \RuntimeException{
public static function wrap(string $fileName, \Exception $e) : self{
return new self("Failed to parse config $fileName: " . $e->getMessage(), 0, $e);
}
}

View File

@ -35,6 +35,11 @@ parameters:
count: 1 count: 1
path: ../../../src/plugin/ScriptPluginLoader.php path: ../../../src/plugin/ScriptPluginLoader.php
-
message: "#^Dead catch \\- JsonException is never thrown in the try block\\.$#"
count: 1
path: ../../../src/utils/Config.php
- -
message: "#^Strict comparison using \\=\\=\\= between string and false will always evaluate to false\\.$#" message: "#^Strict comparison using \\=\\=\\= between string and false will always evaluate to false\\.$#"
count: 1 count: 1