event: harden APIs that accept arrays

plugin devs can't be relied on to pass the proper types to these APIs, and when the wrong types get passed it makes type errors appear from inside the internals.
This commit is contained in:
Dylan K. Taylor 2020-07-04 21:55:23 +01:00
parent 8ce0022de6
commit a34f3261cb
7 changed files with 32 additions and 0 deletions

View File

@ -26,6 +26,7 @@ namespace pocketmine\event\block;
use pocketmine\block\Block; use pocketmine\block\Block;
use pocketmine\event\Cancellable; use pocketmine\event\Cancellable;
use pocketmine\Player; use pocketmine\Player;
use pocketmine\utils\Utils;
use function count; use function count;
/** /**
@ -79,6 +80,7 @@ class SignChangeEvent extends BlockEvent implements Cancellable{
if(count($lines) !== 4){ if(count($lines) !== 4){
throw new \InvalidArgumentException("Array size must be 4!"); throw new \InvalidArgumentException("Array size must be 4!");
} }
Utils::validateArrayValueType($lines, function(string $_) : void{});
$this->lines = $lines; $this->lines = $lines;
} }

View File

@ -25,6 +25,7 @@ namespace pocketmine\event\entity;
use pocketmine\entity\Living; use pocketmine\entity\Living;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\utils\Utils;
/** /**
* @phpstan-extends EntityEvent<Living> * @phpstan-extends EntityEvent<Living>
@ -62,6 +63,7 @@ class EntityDeathEvent extends EntityEvent{
* @param Item[] $drops * @param Item[] $drops
*/ */
public function setDrops(array $drops) : void{ public function setDrops(array $drops) : void{
Utils::validateArrayValueType($drops, function(Item $_) : void{});
$this->drops = $drops; $this->drops = $drops;
} }

View File

@ -27,6 +27,7 @@ use pocketmine\block\Block;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\event\Cancellable; use pocketmine\event\Cancellable;
use pocketmine\level\Position; use pocketmine\level\Position;
use pocketmine\utils\Utils;
/** /**
* Called when a entity explodes * Called when a entity explodes
@ -67,6 +68,7 @@ class EntityExplodeEvent extends EntityEvent implements Cancellable{
* @param Block[] $blocks * @param Block[] $blocks
*/ */
public function setBlockList(array $blocks) : void{ public function setBlockList(array $blocks) : void{
Utils::validateArrayValueType($blocks, function(Block $_) : void{});
$this->blocks = $blocks; $this->blocks = $blocks;
} }

View File

@ -28,6 +28,8 @@ use pocketmine\event\Cancellable;
use pocketmine\permission\PermissionManager; use pocketmine\permission\PermissionManager;
use pocketmine\Player; use pocketmine\Player;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\utils\Utils;
use function array_values;
use function spl_object_id; use function spl_object_id;
/** /**
@ -97,6 +99,7 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
* @param CommandSender[] $recipients * @param CommandSender[] $recipients
*/ */
public function setRecipients(array $recipients) : void{ public function setRecipients(array $recipients) : void{
Utils::validateArrayValueType($recipients, function(CommandSender $_) : void{});
$this->recipients = $recipients; $this->recipients = $recipients;
} }
} }

View File

@ -27,6 +27,7 @@ use pocketmine\Player;
use pocketmine\plugin\Plugin; use pocketmine\plugin\Plugin;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\utils\Binary; use pocketmine\utils\Binary;
use pocketmine\utils\Utils;
use function chr; use function chr;
use function count; use function count;
use function str_replace; use function str_replace;
@ -145,6 +146,7 @@ class QueryRegenerateEvent extends ServerEvent{
* @param Plugin[] $plugins * @param Plugin[] $plugins
*/ */
public function setPlugins(array $plugins) : void{ public function setPlugins(array $plugins) : void{
Utils::validateArrayValueType($plugins, function(Plugin $_) : void{});
$this->plugins = $plugins; $this->plugins = $plugins;
$this->destroyCache(); $this->destroyCache();
} }
@ -160,6 +162,7 @@ class QueryRegenerateEvent extends ServerEvent{
* @param Player[] $players * @param Player[] $players
*/ */
public function setPlayerList(array $players) : void{ public function setPlayerList(array $players) : void{
Utils::validateArrayValueType($players, function(Player $_) : void{});
$this->players = $players; $this->players = $players;
$this->destroyCache(); $this->destroyCache();
} }

View File

@ -685,6 +685,21 @@ class Utils{
} }
} }
/**
* @phpstan-template TMemberType
* @phpstan-param array<mixed, TMemberType> $array
* @phpstan-param \Closure(TMemberType) : void $validator
*/
public static function validateArrayValueType(array $array, \Closure $validator) : void{
foreach($array as $k => $v){
try{
$validator($v);
}catch(\TypeError $e){
throw new \TypeError("Incorrect type of element at \"$k\": " . $e->getMessage(), 0, $e);
}
}
}
public static function recursiveUnlink(string $dir) : void{ public static function recursiveUnlink(string $dir) : void{
if(is_dir($dir)){ if(is_dir($dir)){
$objects = scandir($dir, SCANDIR_SORT_NONE); $objects = scandir($dir, SCANDIR_SORT_NONE);

View File

@ -35,6 +35,11 @@ parameters:
count: 1 count: 1
path: ../../../src/pocketmine/utils/Utils.php path: ../../../src/pocketmine/utils/Utils.php
-
message: "#^Parameter \\#1 \\$ of closure expects TMemberType, TMemberType given\\.$#"
count: 1
path: ../../../src/pocketmine/utils/Utils.php
- -
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotNull\\(\\) with int and string will always evaluate to true\\.$#" message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotNull\\(\\) with int and string will always evaluate to true\\.$#"
count: 1 count: 1