From 0d169b4e80b6746b9157b65c3bd83dc6eedd3903 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 25 Dec 2022 17:13:51 +0000 Subject: [PATCH] Filesystem: added fileGetContents to reduce ErrorToExceptionHandler boilerplate code --- build/make-release.php | 4 ++-- src/Server.php | 5 ++--- src/crafting/CraftingManagerFromDataHelper.php | 5 ++--- .../LegacyToStringBidirectionalIdMap.php | 5 ++--- src/inventory/CreativeInventory.php | 4 ++-- src/item/LegacyStringToItemParser.php | 5 ++--- src/network/mcpe/cache/StaticPacketCache.php | 6 ++---- .../mcpe/convert/GlobalItemTypeDictionary.php | 5 ++--- src/network/mcpe/convert/ItemTranslator.php | 4 ++-- .../mcpe/convert/RuntimeBlockMapping.php | 7 +++---- src/player/DatFilePlayerDataProvider.php | 5 ++--- src/resourcepacks/ResourcePackManager.php | 9 +++------ src/utils/Config.php | 6 +----- src/utils/Filesystem.php | 18 ++++++++++++++++++ src/world/format/io/data/BedrockWorldData.php | 8 ++++---- src/world/format/io/data/JavaWorldData.php | 8 ++++---- 16 files changed, 53 insertions(+), 51 deletions(-) diff --git a/build/make-release.php b/build/make-release.php index 2701c7500e..610dbbe6d9 100644 --- a/build/make-release.php +++ b/build/make-release.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\build\make_release; +use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; use pocketmine\utils\VersionString; use pocketmine\VersionInfo; @@ -30,7 +31,6 @@ use function array_keys; use function array_map; use function dirname; use function fgets; -use function file_get_contents; use function file_put_contents; use function fwrite; use function getopt; @@ -50,7 +50,7 @@ use const STR_PAD_LEFT; require_once dirname(__DIR__) . '/vendor/autoload.php'; function replaceVersion(string $versionInfoPath, string $newVersion, bool $isDev, string $channel) : void{ - $versionInfo = Utils::assumeNotFalse(file_get_contents($versionInfoPath), $versionInfoPath . " should always exist"); + $versionInfo = Filesystem::fileGetContents($versionInfoPath); $versionInfo = preg_replace( $pattern = '/^([\t ]*public )?const BASE_VERSION = "(\d+)\.(\d+)\.(\d+)(?:-(.*))?";$/m', '$1const BASE_VERSION = "' . $newVersion . '";', diff --git a/src/Server.php b/src/Server.php index 4dfa292075..6d762d5384 100644 --- a/src/Server.php +++ b/src/Server.php @@ -125,7 +125,6 @@ use function count; use function date; use function fclose; use function file_exists; -use function file_get_contents; use function file_put_contents; use function filemtime; use function fopen; @@ -777,7 +776,7 @@ class Server{ $this->logger->info("Loading server configuration"); $pocketmineYmlPath = Path::join($this->dataPath, "pocketmine.yml"); if(!file_exists($pocketmineYmlPath)){ - $content = Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml")), "Missing required resource file"); + $content = Filesystem::fileGetContents(Path::join(\pocketmine\RESOURCE_PATH, "pocketmine.yml")); if(VersionInfo::IS_DEVELOPMENT_BUILD){ $content = str_replace("preferred-channel: stable", "preferred-channel: beta", $content); } @@ -950,7 +949,7 @@ class Server{ copy(Path::join(\pocketmine\RESOURCE_PATH, 'plugin_list.yml'), $graylistFile); } try{ - $pluginGraylist = PluginGraylist::fromArray(yaml_parse(file_get_contents($graylistFile))); + $pluginGraylist = PluginGraylist::fromArray(yaml_parse(Filesystem::fileGetContents($graylistFile))); }catch(\InvalidArgumentException $e){ $this->logger->emergency("Failed to load $graylistFile: " . $e->getMessage()); $this->forceShutdownExit(); diff --git a/src/crafting/CraftingManagerFromDataHelper.php b/src/crafting/CraftingManagerFromDataHelper.php index e91cb4ba67..a728ab93d2 100644 --- a/src/crafting/CraftingManagerFromDataHelper.php +++ b/src/crafting/CraftingManagerFromDataHelper.php @@ -26,9 +26,8 @@ namespace pocketmine\crafting; use pocketmine\item\Item; use pocketmine\item\ItemFactory; use pocketmine\utils\AssumptionFailedError; -use pocketmine\utils\Utils; +use pocketmine\utils\Filesystem; use function array_map; -use function file_get_contents; use function is_array; use function json_decode; @@ -52,7 +51,7 @@ final class CraftingManagerFromDataHelper{ } public static function make(string $filePath) : CraftingManager{ - $recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"), true); + $recipes = json_decode(Filesystem::fileGetContents($filePath), true); if(!is_array($recipes)){ throw new AssumptionFailedError("recipes.json root should contain a map of recipe types"); } diff --git a/src/data/bedrock/LegacyToStringBidirectionalIdMap.php b/src/data/bedrock/LegacyToStringBidirectionalIdMap.php index 2ffe1b9868..c5fa272c20 100644 --- a/src/data/bedrock/LegacyToStringBidirectionalIdMap.php +++ b/src/data/bedrock/LegacyToStringBidirectionalIdMap.php @@ -24,8 +24,7 @@ declare(strict_types=1); namespace pocketmine\data\bedrock; use pocketmine\utils\AssumptionFailedError; -use pocketmine\utils\Utils; -use function file_get_contents; +use pocketmine\utils\Filesystem; use function is_array; use function is_int; use function is_string; @@ -45,7 +44,7 @@ abstract class LegacyToStringBidirectionalIdMap{ private array $stringToLegacy = []; public function __construct(string $file){ - $stringToLegacyId = json_decode(Utils::assumeNotFalse(file_get_contents($file), "Missing required resource file"), true); + $stringToLegacyId = json_decode(Filesystem::fileGetContents($file), true); if(!is_array($stringToLegacyId)){ throw new AssumptionFailedError("Invalid format of ID map"); } diff --git a/src/inventory/CreativeInventory.php b/src/inventory/CreativeInventory.php index 980c2e35f1..8037e60917 100644 --- a/src/inventory/CreativeInventory.php +++ b/src/inventory/CreativeInventory.php @@ -25,9 +25,9 @@ namespace pocketmine\inventory; use pocketmine\item\Durable; use pocketmine\item\Item; +use pocketmine\utils\Filesystem; use pocketmine\utils\SingletonTrait; use Symfony\Component\Filesystem\Path; -use function file_get_contents; use function json_decode; final class CreativeInventory{ @@ -37,7 +37,7 @@ final class CreativeInventory{ private array $creative = []; private function __construct(){ - $creativeItems = json_decode(file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, "creativeitems.json")), true); + $creativeItems = json_decode(Filesystem::fileGetContents(Path::join(\pocketmine\BEDROCK_DATA_PATH, "creativeitems.json")), true); foreach($creativeItems as $data){ $item = Item::jsonDeserialize($data); diff --git a/src/item/LegacyStringToItemParser.php b/src/item/LegacyStringToItemParser.php index 9ec97ede8f..1137674a35 100644 --- a/src/item/LegacyStringToItemParser.php +++ b/src/item/LegacyStringToItemParser.php @@ -24,11 +24,10 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Filesystem; use pocketmine\utils\SingletonTrait; -use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; use function explode; -use function file_get_contents; use function is_array; use function is_int; use function is_numeric; @@ -53,7 +52,7 @@ final class LegacyStringToItemParser{ private static function make() : self{ $result = new self(ItemFactory::getInstance()); - $mappingsRaw = Utils::assumeNotFalse(@file_get_contents(Path::join(\pocketmine\RESOURCE_PATH, 'item_from_string_bc_map.json')), "Missing required resource file"); + $mappingsRaw = Filesystem::fileGetContents(Path::join(\pocketmine\RESOURCE_PATH, 'item_from_string_bc_map.json')); $mappings = json_decode($mappingsRaw, true); if(!is_array($mappings)) throw new AssumptionFailedError("Invalid mappings format, expected array"); diff --git a/src/network/mcpe/cache/StaticPacketCache.php b/src/network/mcpe/cache/StaticPacketCache.php index c3eca501d0..4cf878fc39 100644 --- a/src/network/mcpe/cache/StaticPacketCache.php +++ b/src/network/mcpe/cache/StaticPacketCache.php @@ -27,9 +27,9 @@ use pocketmine\network\mcpe\protocol\AvailableActorIdentifiersPacket; use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket; use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; use pocketmine\network\mcpe\protocol\types\CacheableNbt; +use pocketmine\utils\Filesystem; use pocketmine\utils\SingletonTrait; use Symfony\Component\Filesystem\Path; -use function file_get_contents; class StaticPacketCache{ use SingletonTrait; @@ -38,9 +38,7 @@ class StaticPacketCache{ * @phpstan-return CacheableNbt<\pocketmine\nbt\tag\CompoundTag> */ private static function loadCompoundFromFile(string $filePath) : CacheableNbt{ - $rawNbt = @file_get_contents($filePath); - if($rawNbt === false) throw new \RuntimeException("Failed to read file"); - return new CacheableNbt((new NetworkNbtSerializer())->read($rawNbt)->mustGetCompoundTag()); + return new CacheableNbt((new NetworkNbtSerializer())->read(Filesystem::fileGetContents($filePath))->mustGetCompoundTag()); } private static function make() : self{ diff --git a/src/network/mcpe/convert/GlobalItemTypeDictionary.php b/src/network/mcpe/convert/GlobalItemTypeDictionary.php index f33683346f..0795d2fde2 100644 --- a/src/network/mcpe/convert/GlobalItemTypeDictionary.php +++ b/src/network/mcpe/convert/GlobalItemTypeDictionary.php @@ -26,10 +26,9 @@ namespace pocketmine\network\mcpe\convert; use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; use pocketmine\network\mcpe\protocol\types\ItemTypeEntry; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Filesystem; use pocketmine\utils\SingletonTrait; -use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; -use function file_get_contents; use function is_array; use function is_bool; use function is_int; @@ -40,7 +39,7 @@ final class GlobalItemTypeDictionary{ use SingletonTrait; private static function make() : self{ - $data = Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'required_item_list.json')), "Missing required resource file"); + $data = Filesystem::fileGetContents(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'required_item_list.json')); $table = json_decode($data, true); if(!is_array($table)){ throw new AssumptionFailedError("Invalid item list format"); diff --git a/src/network/mcpe/convert/ItemTranslator.php b/src/network/mcpe/convert/ItemTranslator.php index ba13fa7841..375199601a 100644 --- a/src/network/mcpe/convert/ItemTranslator.php +++ b/src/network/mcpe/convert/ItemTranslator.php @@ -26,11 +26,11 @@ namespace pocketmine\network\mcpe\convert; use pocketmine\data\bedrock\LegacyItemIdToStringIdMap; use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Filesystem; use pocketmine\utils\SingletonTrait; use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; use function array_key_exists; -use function file_get_contents; use function is_array; use function is_numeric; use function is_string; @@ -67,7 +67,7 @@ final class ItemTranslator{ private array $complexNetToCoreMapping = []; private static function make() : self{ - $data = Utils::assumeNotFalse(file_get_contents(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'r16_to_current_item_map.json')), "Missing required resource file"); + $data = Filesystem::fileGetContents(Path::join(\pocketmine\BEDROCK_DATA_PATH, 'r16_to_current_item_map.json')); $json = json_decode($data, true); if(!is_array($json) || !isset($json["simple"], $json["complex"]) || !is_array($json["simple"]) || !is_array($json["complex"])){ throw new AssumptionFailedError("Invalid item table format"); diff --git a/src/network/mcpe/convert/RuntimeBlockMapping.php b/src/network/mcpe/convert/RuntimeBlockMapping.php index 547bd0d813..53b146be6f 100644 --- a/src/network/mcpe/convert/RuntimeBlockMapping.php +++ b/src/network/mcpe/convert/RuntimeBlockMapping.php @@ -30,10 +30,9 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; +use pocketmine\utils\Filesystem; use pocketmine\utils\SingletonTrait; -use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; -use function file_get_contents; /** * @internal @@ -57,7 +56,7 @@ final class RuntimeBlockMapping{ public function __construct(string $canonicalBlockStatesFile, string $r12ToCurrentBlockMapFile){ $stream = PacketSerializer::decoder( - Utils::assumeNotFalse(file_get_contents($canonicalBlockStatesFile), "Missing required resource file"), + Filesystem::fileGetContents($canonicalBlockStatesFile), 0, new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary()) ); @@ -75,7 +74,7 @@ final class RuntimeBlockMapping{ /** @var R12ToCurrentBlockMapEntry[] $legacyStateMap */ $legacyStateMap = []; $legacyStateMapReader = PacketSerializer::decoder( - Utils::assumeNotFalse(file_get_contents($r12ToCurrentBlockMapFile), "Missing required resource file"), + Filesystem::fileGetContents($r12ToCurrentBlockMapFile), 0, new PacketSerializerContext(GlobalItemTypeDictionary::getInstance()->getDictionary()) ); diff --git a/src/player/DatFilePlayerDataProvider.php b/src/player/DatFilePlayerDataProvider.php index 53388dcebb..13b5ea423b 100644 --- a/src/player/DatFilePlayerDataProvider.php +++ b/src/player/DatFilePlayerDataProvider.php @@ -32,7 +32,6 @@ use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; use Symfony\Component\Filesystem\Path; use function file_exists; -use function file_get_contents; use function rename; use function strtolower; use function zlib_decode; @@ -70,8 +69,8 @@ final class DatFilePlayerDataProvider implements PlayerDataProvider{ } try{ - $contents = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents($path)); - }catch(\ErrorException $e){ + $contents = Filesystem::fileGetContents($path); + }catch(\RuntimeException $e){ throw new PlayerDataLoadException("Failed to read player data file \"$path\": " . $e->getMessage(), 0, $e); } try{ diff --git a/src/resourcepacks/ResourcePackManager.php b/src/resourcepacks/ResourcePackManager.php index 655f3a3a99..6429ac06bb 100644 --- a/src/resourcepacks/ResourcePackManager.php +++ b/src/resourcepacks/ResourcePackManager.php @@ -23,14 +23,13 @@ declare(strict_types=1); namespace pocketmine\resourcepacks; -use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\utils\Config; +use pocketmine\utils\Filesystem; use Symfony\Component\Filesystem\Path; use function array_keys; use function copy; use function count; use function file_exists; -use function file_get_contents; use function gettype; use function is_array; use function is_dir; @@ -104,10 +103,8 @@ class ResourcePackManager{ $keyPath = Path::join($this->path, $pack . ".key"); if(file_exists($keyPath)){ try{ - $key = ErrorToExceptionHandler::trapAndRemoveFalse( - fn() => file_get_contents($keyPath) - ); - }catch(\ErrorException $e){ + $key = Filesystem::fileGetContents($keyPath); + }catch(\RuntimeException $e){ throw new ResourcePackException("Could not read encryption key file: " . $e->getMessage(), 0, $e); } $key = rtrim($key, "\r\n"); diff --git a/src/utils/Config.php b/src/utils/Config.php index 617feeacf6..670d6bda32 100644 --- a/src/utils/Config.php +++ b/src/utils/Config.php @@ -33,7 +33,6 @@ use function count; use function date; use function explode; use function file_exists; -use function file_get_contents; use function get_debug_type; use function implode; use function is_array; @@ -162,10 +161,7 @@ class Config{ $this->config = $default; $this->save(); }else{ - $content = file_get_contents($this->file); - if($content === false){ - throw new \RuntimeException("Unable to load config file"); - } + $content = Filesystem::fileGetContents($this->file); switch($this->type){ case Config::PROPERTIES: $config = self::parseProperties($content); diff --git a/src/utils/Filesystem.php b/src/utils/Filesystem.php index 0a183d4cd1..71de8bc34f 100644 --- a/src/utils/Filesystem.php +++ b/src/utils/Filesystem.php @@ -30,6 +30,7 @@ use function dirname; use function fclose; use function fflush; use function file_exists; +use function file_get_contents; use function file_put_contents; use function flock; use function fopen; @@ -296,4 +297,21 @@ final class Filesystem{ @unlink($temporaryFileName); } } + + /** + * Wrapper around file_get_contents() which throws an exception instead of generating E_* errors. + * + * @phpstan-param resource|null $context + * @phpstan-param 0|positive-int $offset + * @phpstan-param 0|positive-int|null $length + * + * @throws \RuntimeException + */ + public static function fileGetContents(string $fileName, bool $useIncludePath = false, $context = null, int $offset = 0, ?int $length = null) : string{ + try{ + return ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents($fileName, $useIncludePath, $context, $offset, $length)); + }catch(\ErrorException $e){ + throw new \RuntimeException("Failed to read file $fileName: " . $e->getMessage(), 0, $e); + } + } } diff --git a/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index 995f648982..d6f1b2e105 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -39,7 +39,6 @@ use pocketmine\world\generator\GeneratorManager; use pocketmine\world\World; use pocketmine\world\WorldCreationOptions; use Symfony\Component\Filesystem\Path; -use function file_get_contents; use function file_put_contents; use function strlen; use function substr; @@ -111,9 +110,10 @@ class BedrockWorldData extends BaseNbtWorldData{ } protected function load() : CompoundTag{ - $rawLevelData = @file_get_contents($this->dataPath); - if($rawLevelData === false){ - throw new CorruptedWorldException("Failed to read level.dat (permission denied or doesn't exist)"); + try{ + $rawLevelData = Filesystem::fileGetContents($this->dataPath); + }catch(\RuntimeException $e){ + throw new CorruptedWorldException($e->getMessage(), 0, $e); } if(strlen($rawLevelData) <= 8){ throw new CorruptedWorldException("Truncated level.dat"); diff --git a/src/world/format/io/data/JavaWorldData.php b/src/world/format/io/data/JavaWorldData.php index e53d857ad8..377480c9ab 100644 --- a/src/world/format/io/data/JavaWorldData.php +++ b/src/world/format/io/data/JavaWorldData.php @@ -37,7 +37,6 @@ use pocketmine\world\World; use pocketmine\world\WorldCreationOptions; use Symfony\Component\Filesystem\Path; use function ceil; -use function file_get_contents; use function file_put_contents; use function microtime; use function zlib_decode; @@ -75,9 +74,10 @@ class JavaWorldData extends BaseNbtWorldData{ } protected function load() : CompoundTag{ - $rawLevelData = @file_get_contents($this->dataPath); - if($rawLevelData === false){ - throw new CorruptedWorldException("Failed to read level.dat (permission denied or doesn't exist)"); + try{ + $rawLevelData = Filesystem::fileGetContents($this->dataPath); + }catch(\RuntimeException $e){ + throw new CorruptedWorldException($e->getMessage(), 0, $e); } $nbt = new BigEndianNbtSerializer(); $decompressed = @zlib_decode($rawLevelData);