mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-19 15:35:52 +00:00
Merge branch 'stable' into minor-next
This commit is contained in:
commit
12ae8dc03b
@ -122,7 +122,7 @@ if(!is_array($ids)){
|
||||
throw new \RuntimeException("Invalid biome ID map, expected array for root JSON object");
|
||||
}
|
||||
$cleanedIds = [];
|
||||
foreach($ids as $name => $id){
|
||||
foreach(Utils::promoteKeys($ids) as $name => $id){
|
||||
if(!is_string($name) || !is_int($id)){
|
||||
throw new \RuntimeException("Invalid biome ID map, expected string => int map");
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ $constants = [];
|
||||
* @phpstan-param-out array<string, string> $constants
|
||||
*/
|
||||
function collectProperties(string $prefix, array $properties, array &$constants) : void{
|
||||
foreach($properties as $propertyName => $property){
|
||||
foreach(Utils::promoteKeys($properties) as $propertyName => $property){
|
||||
$fullPropertyName = ($prefix !== "" ? $prefix . "." : "") . $propertyName;
|
||||
|
||||
$constName = str_replace([".", "-"], "_", strtoupper($fullPropertyName));
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\build\update_registry_annotations;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use function basename;
|
||||
use function class_exists;
|
||||
use function count;
|
||||
@ -48,6 +49,7 @@ if(count($argv) !== 2){
|
||||
|
||||
/**
|
||||
* @param object[] $members
|
||||
* @phpstan-param array<string, object> $members
|
||||
*/
|
||||
function generateMethodAnnotations(string $namespaceName, array $members) : string{
|
||||
$selfName = basename(__FILE__);
|
||||
@ -60,7 +62,7 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
|
||||
|
||||
static $lineTmpl = " * @method static %2\$s %s()";
|
||||
$memberLines = [];
|
||||
foreach($members as $name => $member){
|
||||
foreach(Utils::stringifyKeys($members) as $name => $member){
|
||||
$reflect = new \ReflectionClass($member);
|
||||
while($reflect !== false && $reflect->isAnonymous()){
|
||||
$reflect = $reflect->getParentClass();
|
||||
|
@ -332,7 +332,7 @@ class MemoryManager{
|
||||
continue;
|
||||
}
|
||||
$methodStatics = [];
|
||||
foreach($method->getStaticVariables() as $name => $variable){
|
||||
foreach(Utils::promoteKeys($method->getStaticVariables()) as $name => $variable){
|
||||
$methodStatics[$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
if(count($methodStatics) > 0){
|
||||
@ -360,7 +360,7 @@ class MemoryManager{
|
||||
'_SESSION' => true
|
||||
];
|
||||
|
||||
foreach($GLOBALS as $varName => $value){
|
||||
foreach(Utils::promoteKeys($GLOBALS) as $varName => $value){
|
||||
if(isset($ignoredGlobals[$varName])){
|
||||
continue;
|
||||
}
|
||||
@ -376,7 +376,7 @@ class MemoryManager{
|
||||
$reflect = new \ReflectionFunction($function);
|
||||
|
||||
$vars = [];
|
||||
foreach($reflect->getStaticVariables() as $varName => $variable){
|
||||
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $varName => $variable){
|
||||
$vars[$varName] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
if(count($vars) > 0){
|
||||
@ -416,7 +416,7 @@ class MemoryManager{
|
||||
$info["this"] = self::continueDump($closureThis, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
|
||||
foreach($reflect->getStaticVariables() as $name => $variable){
|
||||
foreach(Utils::promoteKeys($reflect->getStaticVariables()) as $name => $variable){
|
||||
$info["referencedVars"][$name] = self::continueDump($variable, $objects, $refCounts, 0, $maxNesting, $maxStringSize);
|
||||
}
|
||||
}else{
|
||||
@ -499,7 +499,7 @@ class MemoryManager{
|
||||
}
|
||||
$data = [];
|
||||
$numeric = 0;
|
||||
foreach($from as $key => $value){
|
||||
foreach(Utils::promoteKeys($from) as $key => $value){
|
||||
$data[$numeric] = [
|
||||
"k" => self::continueDump($key, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
|
||||
"v" => self::continueDump($value, $objects, $refCounts, $recursion + 1, $maxNesting, $maxStringSize),
|
||||
|
@ -736,12 +736,15 @@ class Server{
|
||||
|
||||
/**
|
||||
* @return string[][]
|
||||
* @phpstan-return array<string, list<string>>
|
||||
*/
|
||||
public function getCommandAliases() : array{
|
||||
$section = $this->configGroup->getProperty(Yml::ALIASES);
|
||||
$result = [];
|
||||
if(is_array($section)){
|
||||
foreach($section as $key => $value){
|
||||
foreach(Utils::promoteKeys($section) as $key => $value){
|
||||
//TODO: more validation needed here
|
||||
//key might not be a string, value might not be list<string>
|
||||
$commands = [];
|
||||
if(is_array($value)){
|
||||
$commands = $value;
|
||||
@ -749,7 +752,7 @@ class Server{
|
||||
$commands[] = (string) $value;
|
||||
}
|
||||
|
||||
$result[$key] = $commands;
|
||||
$result[(string) $key] = $commands;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1094,7 +1097,11 @@ class Server{
|
||||
|
||||
$anyWorldFailedToLoad = false;
|
||||
|
||||
foreach((array) $this->configGroup->getProperty(Yml::WORLDS, []) as $name => $options){
|
||||
foreach(Utils::promoteKeys((array) $this->configGroup->getProperty(Yml::WORLDS, [])) as $name => $options){
|
||||
if(!is_string($name)){
|
||||
//TODO: this probably should be an error
|
||||
continue;
|
||||
}
|
||||
if($options === null){
|
||||
$options = [];
|
||||
}elseif(!is_array($options)){
|
||||
|
@ -132,6 +132,7 @@ class RuntimeBlockStateRegistry{
|
||||
|
||||
/**
|
||||
* @return Block[]
|
||||
* @phpstan-return array<int, Block>
|
||||
*/
|
||||
public function getAllKnownStates() : array{
|
||||
return $this->fullList;
|
||||
|
@ -39,7 +39,10 @@ class EnchantInventory extends SimpleInventory implements BlockInventory, Tempor
|
||||
public const SLOT_INPUT = 0;
|
||||
public const SLOT_LAPIS = 1;
|
||||
|
||||
/** @var EnchantingOption[] $options */
|
||||
/**
|
||||
* @var EnchantingOption[] $options
|
||||
* @phpstan-var list<EnchantingOption>
|
||||
*/
|
||||
private array $options = [];
|
||||
|
||||
public function __construct(Position $holder){
|
||||
|
@ -36,13 +36,17 @@ use function str_contains;
|
||||
class SignText{
|
||||
public const LINE_COUNT = 4;
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var array{0: string, 1: string, 2: string, 3: string}
|
||||
*/
|
||||
private array $lines;
|
||||
private Color $baseColor;
|
||||
private bool $glowing;
|
||||
|
||||
/**
|
||||
* @param string[]|null $lines index-sensitive; keys 0-3 will be used, regardless of array order
|
||||
* @phpstan-param array{0?: string, 1?: string, 2?: string, 3?: string}|null $lines
|
||||
*
|
||||
* @throws \InvalidArgumentException if the array size is greater than 4
|
||||
* @throws \InvalidArgumentException if invalid keys (out of bounds or string) are found in the array
|
||||
@ -82,6 +86,7 @@ class SignText{
|
||||
* Returns an array of lines currently on the sign.
|
||||
*
|
||||
* @return string[]
|
||||
* @phpstan-return array{0: string, 1: string, 2: string, 3: string}
|
||||
*/
|
||||
public function getLines() : array{
|
||||
return $this->lines;
|
||||
|
@ -44,10 +44,16 @@ abstract class Command{
|
||||
private string $nextLabel;
|
||||
private string $label;
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private array $aliases = [];
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private array $activeAliases = [];
|
||||
|
||||
private ?CommandMap $commandMap = null;
|
||||
@ -62,6 +68,7 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @param string[] $aliases
|
||||
* @phpstan-param list<string> $aliases
|
||||
*/
|
||||
public function __construct(string $name, Translatable|string $description = "", Translatable|string|null $usageMessage = null, array $aliases = []){
|
||||
$this->name = $name;
|
||||
@ -182,6 +189,7 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getAliases() : array{
|
||||
return $this->activeAliases;
|
||||
@ -201,6 +209,7 @@ abstract class Command{
|
||||
|
||||
/**
|
||||
* @param string[] $aliases
|
||||
* @phpstan-param list<string> $aliases
|
||||
*/
|
||||
public function setAliases(array $aliases) : void{
|
||||
$this->aliases = $aliases;
|
||||
|
@ -71,6 +71,7 @@ use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_shift;
|
||||
use function count;
|
||||
use function implode;
|
||||
@ -81,7 +82,10 @@ use function trim;
|
||||
|
||||
class SimpleCommandMap implements CommandMap{
|
||||
|
||||
/** @var Command[] */
|
||||
/**
|
||||
* @var Command[]
|
||||
* @phpstan-var array<string, Command>
|
||||
*/
|
||||
protected array $knownCommands = [];
|
||||
|
||||
public function __construct(private Server $server){
|
||||
@ -171,7 +175,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
}
|
||||
|
||||
public function unregister(Command $command) : bool{
|
||||
foreach($this->knownCommands as $lbl => $cmd){
|
||||
foreach(Utils::promoteKeys($this->knownCommands) as $lbl => $cmd){
|
||||
if($cmd === $command){
|
||||
unset($this->knownCommands[$lbl]);
|
||||
}
|
||||
@ -239,6 +243,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
|
||||
/**
|
||||
* @return Command[]
|
||||
* @phpstan-return array<string, Command>
|
||||
*/
|
||||
public function getCommands() : array{
|
||||
return $this->knownCommands;
|
||||
@ -247,7 +252,7 @@ class SimpleCommandMap implements CommandMap{
|
||||
public function registerServerAliases() : void{
|
||||
$values = $this->server->getCommandAliases();
|
||||
|
||||
foreach($values as $alias => $commandStrings){
|
||||
foreach(Utils::stringifyKeys($values) as $alias => $commandStrings){
|
||||
if(str_contains($alias, ":")){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_alias_illegal($alias)));
|
||||
continue;
|
||||
|
@ -110,14 +110,15 @@ class CraftingManager{
|
||||
|
||||
/**
|
||||
* @param Item[] $items
|
||||
* @phpstan-param list<Item> $items
|
||||
*
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
private static function pack(array $items) : array{
|
||||
/** @var Item[] $result */
|
||||
$result = [];
|
||||
|
||||
foreach($items as $i => $item){
|
||||
foreach($items as $item){
|
||||
foreach($result as $otherItem){
|
||||
if($item->canStackWith($otherItem)){
|
||||
$otherItem->setCount($otherItem->getCount() + $item->getCount());
|
||||
@ -134,6 +135,7 @@ class CraftingManager{
|
||||
|
||||
/**
|
||||
* @param Item[] $outputs
|
||||
* @phpstan-param list<Item> $outputs
|
||||
*/
|
||||
private static function hashOutputs(array $outputs) : string{
|
||||
$outputs = self::pack($outputs);
|
||||
|
@ -193,7 +193,7 @@ final class CraftingManagerFromDataHelper{
|
||||
*/
|
||||
private static function loadJsonObjectListIntoModel(\JsonMapper $mapper, string $modelClass, array $data) : array{
|
||||
$result = [];
|
||||
foreach($data as $i => $item){
|
||||
foreach(Utils::promoteKeys($data) as $i => $item){
|
||||
if(!is_object($item)){
|
||||
throw new SavedDataLoadingException("Invalid entry at index $i: expected object, got " . get_debug_type($item));
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ interface CraftingRecipe{
|
||||
* Returns a list of items needed to craft this recipe. This MUST NOT include Air items or items with a zero count.
|
||||
*
|
||||
* @return RecipeIngredient[]
|
||||
* @phpstan-return list<RecipeIngredient>
|
||||
*/
|
||||
public function getIngredientList() : array;
|
||||
|
||||
@ -37,6 +38,7 @@ interface CraftingRecipe{
|
||||
* Returns a list of results this recipe will produce when the inputs in the given crafting grid are consumed.
|
||||
*
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
public function getResultsFor(CraftingGrid $grid) : array;
|
||||
|
||||
|
@ -32,11 +32,20 @@ use function str_contains;
|
||||
use function strlen;
|
||||
|
||||
class ShapedRecipe implements CraftingRecipe{
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private array $shape = [];
|
||||
/** @var RecipeIngredient[] char => RecipeIngredient map */
|
||||
/**
|
||||
* @var RecipeIngredient[] char => RecipeIngredient map
|
||||
* @phpstan-var array<string, RecipeIngredient>
|
||||
*/
|
||||
private array $ingredientList = [];
|
||||
/** @var Item[] */
|
||||
/**
|
||||
* @var Item[]
|
||||
* @phpstan-var list<Item>
|
||||
*/
|
||||
private array $results = [];
|
||||
|
||||
private int $height;
|
||||
@ -56,6 +65,10 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
* @param Item[] $results List of items that this recipe produces when crafted.
|
||||
*
|
||||
* Note: Recipes **do not** need to be square. Do NOT add padding for empty rows/columns.
|
||||
*
|
||||
* @phpstan-param list<string> $shape
|
||||
* @phpstan-param array<string, RecipeIngredient> $ingredients
|
||||
* @phpstan-param list<Item> $results
|
||||
*/
|
||||
public function __construct(array $shape, array $ingredients, array $results){
|
||||
$this->height = count($shape);
|
||||
@ -84,7 +97,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
|
||||
$this->shape = $shape;
|
||||
|
||||
foreach($ingredients as $char => $i){
|
||||
foreach(Utils::stringifyKeys($ingredients) as $char => $i){
|
||||
if(!str_contains(implode($this->shape), $char)){
|
||||
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
|
||||
}
|
||||
@ -105,6 +118,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
public function getResults() : array{
|
||||
return Utils::cloneObjectArray($this->results);
|
||||
@ -112,6 +126,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
public function getResultsFor(CraftingGrid $grid) : array{
|
||||
return $this->getResults();
|
||||
@ -119,6 +134,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
|
||||
/**
|
||||
* @return (RecipeIngredient|null)[][]
|
||||
* @phpstan-return list<list<RecipeIngredient|null>>
|
||||
*/
|
||||
public function getIngredientMap() : array{
|
||||
$ingredients = [];
|
||||
@ -132,9 +148,6 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
return $ingredients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecipeIngredient[]
|
||||
*/
|
||||
public function getIngredientList() : array{
|
||||
$ingredients = [];
|
||||
|
||||
@ -157,6 +170,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
/**
|
||||
* Returns an array of strings containing characters representing the recipe's shape.
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getShape() : array{
|
||||
return $this->shape;
|
||||
|
@ -28,15 +28,24 @@ use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
|
||||
class ShapelessRecipe implements CraftingRecipe{
|
||||
/** @var RecipeIngredient[] */
|
||||
/**
|
||||
* @var RecipeIngredient[]
|
||||
* @phpstan-var list<RecipeIngredient>
|
||||
*/
|
||||
private array $ingredients = [];
|
||||
/** @var Item[] */
|
||||
/**
|
||||
* @var Item[]
|
||||
* @phpstan-var list<Item>
|
||||
*/
|
||||
private array $results;
|
||||
private ShapelessRecipeType $type;
|
||||
|
||||
/**
|
||||
* @param RecipeIngredient[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array.
|
||||
* @param Item[] $results List of result items created by this recipe.
|
||||
*
|
||||
* @phpstan-param list<RecipeIngredient> $ingredients
|
||||
* @phpstan-param list<Item> $results
|
||||
*/
|
||||
public function __construct(array $ingredients, array $results, ShapelessRecipeType $type){
|
||||
$this->type = $type;
|
||||
@ -50,6 +59,7 @@ class ShapelessRecipe implements CraftingRecipe{
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
public function getResults() : array{
|
||||
return Utils::cloneObjectArray($this->results);
|
||||
@ -63,9 +73,6 @@ class ShapelessRecipe implements CraftingRecipe{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecipeIngredient[]
|
||||
*/
|
||||
public function getIngredientList() : array{
|
||||
return $this->ingredients;
|
||||
}
|
||||
|
@ -43,7 +43,10 @@ final class CrashDumpData implements \JsonSerializable{
|
||||
|
||||
public string $plugin = "";
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var array<int, string>
|
||||
*/
|
||||
public array $code = [];
|
||||
|
||||
/** @var string[] */
|
||||
@ -55,7 +58,10 @@ final class CrashDumpData implements \JsonSerializable{
|
||||
*/
|
||||
public array $plugins = [];
|
||||
|
||||
/** @var string[] */
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
public array $parameters = [];
|
||||
|
||||
public string $serverDotProperties = "";
|
||||
|
@ -48,7 +48,7 @@ final class ItemTagToIdMap{
|
||||
throw new AssumptionFailedError("Invalid item tag map, expected array");
|
||||
}
|
||||
$cleanMap = [];
|
||||
foreach($map as $tagName => $ids){
|
||||
foreach(Utils::promoteKeys($map) as $tagName => $ids){
|
||||
if(!is_string($tagName)){
|
||||
throw new AssumptionFailedError("Invalid item tag name $tagName, expected string as key");
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\data\bedrock;
|
||||
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function is_string;
|
||||
@ -43,7 +44,7 @@ abstract class LegacyToStringIdMap{
|
||||
if(!is_array($stringToLegacyId)){
|
||||
throw new AssumptionFailedError("Invalid format of ID map");
|
||||
}
|
||||
foreach($stringToLegacyId as $stringId => $legacyId){
|
||||
foreach(Utils::promoteKeys($stringToLegacyId) as $stringId => $legacyId){
|
||||
if(!is_string($stringId) || !is_int($legacyId)){
|
||||
throw new AssumptionFailedError("ID map should have string keys and int values");
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ final class R12ItemIdToBlockIdMap{
|
||||
}
|
||||
|
||||
$builtMap = [];
|
||||
foreach($map as $itemId => $blockId){
|
||||
foreach(Utils::promoteKeys($map) as $itemId => $blockId){
|
||||
if(!is_string($itemId)){
|
||||
throw new AssumptionFailedError("Invalid blockitem ID mapping table, expected string as key");
|
||||
}
|
||||
|
@ -103,7 +103,10 @@ abstract class Entity{
|
||||
return self::$entityCount++;
|
||||
}
|
||||
|
||||
/** @var Player[] */
|
||||
/**
|
||||
* @var Player[]
|
||||
* @phpstan-var array<int, Player>
|
||||
*/
|
||||
protected array $hasSpawned = [];
|
||||
|
||||
protected int $id;
|
||||
|
@ -30,7 +30,10 @@ use function spl_object_id;
|
||||
use const SORT_NUMERIC;
|
||||
|
||||
class HandlerList{
|
||||
/** @var RegisteredListener[][] */
|
||||
/**
|
||||
* @var RegisteredListener[][]
|
||||
* @phpstan-var array<int, array<int, RegisteredListener>>
|
||||
*/
|
||||
private array $handlerSlots = [];
|
||||
|
||||
/** @var RegisteredListenerCache[] */
|
||||
|
@ -41,7 +41,10 @@ use function spl_object_id;
|
||||
*/
|
||||
abstract class BaseInventory implements Inventory, SlotValidatedInventory{
|
||||
protected int $maxStackSize = Inventory::MAX_STACK;
|
||||
/** @var Player[] */
|
||||
/**
|
||||
* @var Player[]
|
||||
* @phpstan-var array<int, Player>
|
||||
*/
|
||||
protected array $viewers = [];
|
||||
/**
|
||||
* @var InventoryListener[]|ObjectSet
|
||||
@ -286,8 +289,6 @@ abstract class BaseInventory implements Inventory, SlotValidatedInventory{
|
||||
}
|
||||
|
||||
public function removeItem(Item ...$slots) : array{
|
||||
/** @var Item[] $searchItems */
|
||||
/** @var Item[] $slots */
|
||||
$searchItems = [];
|
||||
foreach($slots as $slot){
|
||||
if(!$slot->isNull()){
|
||||
|
@ -36,7 +36,10 @@ final class CreativeInventory{
|
||||
use SingletonTrait;
|
||||
use DestructorCallbackTrait;
|
||||
|
||||
/** @var Item[] */
|
||||
/**
|
||||
* @var Item[]
|
||||
* @phpstan-var array<int, Item>
|
||||
*/
|
||||
private array $creative = [];
|
||||
|
||||
/** @phpstan-var ObjectSet<\Closure() : void> */
|
||||
@ -69,6 +72,7 @@ final class CreativeInventory{
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @phpstan-return array<int, Item>
|
||||
*/
|
||||
public function getAll() : array{
|
||||
return Utils::cloneObjectArray($this->creative);
|
||||
|
@ -57,9 +57,15 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
protected ?CraftingRecipe $recipe = null;
|
||||
protected ?int $repetitions = null;
|
||||
|
||||
/** @var Item[] */
|
||||
/**
|
||||
* @var Item[]
|
||||
* @phpstan-var list<Item>
|
||||
*/
|
||||
protected array $inputs = [];
|
||||
/** @var Item[] */
|
||||
/**
|
||||
* @var Item[]
|
||||
* @phpstan-var list<Item>
|
||||
*/
|
||||
protected array $outputs = [];
|
||||
|
||||
private CraftingManager $craftingManager;
|
||||
@ -74,6 +80,9 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
/**
|
||||
* @param Item[] $providedItems
|
||||
* @return Item[]
|
||||
*
|
||||
* @phpstan-param list<Item> $providedItems
|
||||
* @phpstan-return list<Item>
|
||||
*/
|
||||
private static function packItems(array $providedItems) : array{
|
||||
$packedProvidedItems = [];
|
||||
@ -94,6 +103,9 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
/**
|
||||
* @param Item[] $providedItems
|
||||
* @param RecipeIngredient[] $recipeIngredients
|
||||
*
|
||||
* @phpstan-param list<Item> $providedItems
|
||||
* @phpstan-param list<RecipeIngredient> $recipeIngredients
|
||||
*/
|
||||
public static function matchIngredients(array $providedItems, array $recipeIngredients, int $expectedIterations) : void{
|
||||
if(count($recipeIngredients) === 0){
|
||||
@ -172,6 +184,9 @@ class CraftingTransaction extends InventoryTransaction{
|
||||
* @param Item[] $txItems
|
||||
* @param Item[] $recipeItems
|
||||
*
|
||||
* @phpstan-param list<Item> $txItems
|
||||
* @phpstan-param list<Item> $recipeItems
|
||||
*
|
||||
* @throws TransactionValidationException
|
||||
*/
|
||||
protected function matchOutputs(array $txItems, array $recipeItems) : int{
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\inventory\transaction\action\InventoryAction;
|
||||
use pocketmine\inventory\transaction\action\SlotChangeAction;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_keys;
|
||||
use function array_values;
|
||||
use function assert;
|
||||
@ -57,10 +58,16 @@ use function spl_object_id;
|
||||
class InventoryTransaction{
|
||||
protected bool $hasExecuted = false;
|
||||
|
||||
/** @var Inventory[] */
|
||||
/**
|
||||
* @var Inventory[]
|
||||
* @phpstan-var array<int, Inventory>
|
||||
*/
|
||||
protected array $inventories = [];
|
||||
|
||||
/** @var InventoryAction[] */
|
||||
/**
|
||||
* @var InventoryAction[]
|
||||
* @phpstan-var array<int, InventoryAction>
|
||||
*/
|
||||
protected array $actions = [];
|
||||
|
||||
/**
|
||||
@ -81,6 +88,7 @@ class InventoryTransaction{
|
||||
|
||||
/**
|
||||
* @return Inventory[]
|
||||
* @phpstan-return array<int, Inventory>
|
||||
*/
|
||||
public function getInventories() : array{
|
||||
return $this->inventories;
|
||||
@ -93,6 +101,7 @@ class InventoryTransaction{
|
||||
* significance and should not be relied on.
|
||||
*
|
||||
* @return InventoryAction[]
|
||||
* @phpstan-return array<int, InventoryAction>
|
||||
*/
|
||||
public function getActions() : array{
|
||||
return $this->actions;
|
||||
@ -133,8 +142,8 @@ class InventoryTransaction{
|
||||
/**
|
||||
* @param Item[] $needItems
|
||||
* @param Item[] $haveItems
|
||||
* @phpstan-param-out Item[] $needItems
|
||||
* @phpstan-param-out Item[] $haveItems
|
||||
* @phpstan-param-out list<Item> $needItems
|
||||
* @phpstan-param-out list<Item> $haveItems
|
||||
*
|
||||
* @throws TransactionValidationException
|
||||
*/
|
||||
@ -188,11 +197,8 @@ class InventoryTransaction{
|
||||
* wrong order), so this method also tries to chain them into order.
|
||||
*/
|
||||
protected function squashDuplicateSlotChanges() : void{
|
||||
/** @var SlotChangeAction[][] $slotChanges */
|
||||
$slotChanges = [];
|
||||
/** @var Inventory[] $inventories */
|
||||
$inventories = [];
|
||||
/** @var int[] $slots */
|
||||
$slots = [];
|
||||
|
||||
foreach($this->actions as $key => $action){
|
||||
@ -203,7 +209,7 @@ class InventoryTransaction{
|
||||
}
|
||||
}
|
||||
|
||||
foreach($slotChanges as $hash => $list){
|
||||
foreach(Utils::stringifyKeys($slotChanges) as $hash => $list){
|
||||
if(count($list) === 1){ //No need to compact slot changes if there is only one on this slot
|
||||
continue;
|
||||
}
|
||||
@ -233,6 +239,7 @@ class InventoryTransaction{
|
||||
|
||||
/**
|
||||
* @param SlotChangeAction[] $possibleActions
|
||||
* @phpstan-param list<SlotChangeAction> $possibleActions
|
||||
*/
|
||||
protected function findResultItem(Item $needOrigin, array $possibleActions) : ?Item{
|
||||
assert(count($possibleActions) > 0);
|
||||
|
@ -33,7 +33,10 @@ use function spl_object_id;
|
||||
* The primary purpose of this trait is providing scope isolation for the methods it contains.
|
||||
*/
|
||||
trait ItemEnchantmentHandlingTrait{
|
||||
/** @var EnchantmentInstance[] */
|
||||
/**
|
||||
* @var EnchantmentInstance[]
|
||||
* @phpstan-var array<int, EnchantmentInstance>
|
||||
*/
|
||||
protected array $enchantments = [];
|
||||
|
||||
public function hasEnchantments() : bool{
|
||||
@ -79,6 +82,7 @@ trait ItemEnchantmentHandlingTrait{
|
||||
|
||||
/**
|
||||
* @return EnchantmentInstance[]
|
||||
* @phpstan-return array<int, EnchantmentInstance>
|
||||
*/
|
||||
public function getEnchantments() : array{
|
||||
return $this->enchantments;
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\data\bedrock\item\upgrade\ItemDataUpgrader;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\format\io\GlobalItemDataHandlers;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function explode;
|
||||
@ -67,7 +68,7 @@ final class LegacyStringToItemParser{
|
||||
$mappings = json_decode($mappingsRaw, true);
|
||||
if(!is_array($mappings)) throw new AssumptionFailedError("Invalid mappings format, expected array");
|
||||
|
||||
foreach($mappings as $name => $id){
|
||||
foreach(Utils::promoteKeys($mappings) as $name => $id){
|
||||
if(!is_string($id)) throw new AssumptionFailedError("Invalid mappings format, expected string values");
|
||||
$result->addMapping((string) $name, $id);
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ class Language{
|
||||
$baseText = $this->parseTranslation($str, $onlyPrefix);
|
||||
}
|
||||
|
||||
foreach($params as $i => $p){
|
||||
foreach(Utils::promoteKeys($params) as $i => $p){
|
||||
$replacement = $p instanceof Translatable ? $this->translate($p) : (string) $p;
|
||||
$baseText = str_replace("{%$i}", $replacement, $baseText);
|
||||
}
|
||||
@ -161,7 +161,7 @@ class Language{
|
||||
$baseText = $this->parseTranslation($c->getText());
|
||||
}
|
||||
|
||||
foreach($c->getParameters() as $i => $p){
|
||||
foreach(Utils::promoteKeys($c->getParameters()) as $i => $p){
|
||||
$replacement = $p instanceof Translatable ? $this->translate($p) : $p;
|
||||
$baseText = str_replace("{%$i}", $replacement, $baseText);
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\lang;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
final class Translatable{
|
||||
/** @var string[]|Translatable[] $params */
|
||||
protected array $params = [];
|
||||
@ -34,7 +36,7 @@ final class Translatable{
|
||||
protected string $text,
|
||||
array $params = []
|
||||
){
|
||||
foreach($params as $k => $param){
|
||||
foreach(Utils::promoteKeys($params) as $k => $param){
|
||||
if(!($param instanceof Translatable)){
|
||||
$this->params[$k] = (string) $param;
|
||||
}else{
|
||||
|
@ -30,10 +30,16 @@ use function spl_object_id;
|
||||
|
||||
class NetworkSessionManager{
|
||||
|
||||
/** @var NetworkSession[] */
|
||||
/**
|
||||
* @var NetworkSession[]
|
||||
* @phpstan-var array<int, NetworkSession>
|
||||
*/
|
||||
private array $sessions = [];
|
||||
|
||||
/** @var NetworkSession[] */
|
||||
/**
|
||||
* @var NetworkSession[]
|
||||
* @phpstan-var array<int, NetworkSession>
|
||||
*/
|
||||
private array $pendingLoginSessions = [];
|
||||
|
||||
/**
|
||||
|
@ -695,6 +695,7 @@ class InventoryManager{
|
||||
|
||||
/**
|
||||
* @param EnchantingOption[] $options
|
||||
* @phpstan-param list<EnchantingOption> $options
|
||||
*/
|
||||
public function syncEnchantingTableOptions(array $options) : void{
|
||||
$protocolOptions = [];
|
||||
|
@ -1092,7 +1092,7 @@ class NetworkSession{
|
||||
|
||||
public function syncAvailableCommands() : void{
|
||||
$commandData = [];
|
||||
foreach($this->server->getCommandMap()->getCommands() as $name => $command){
|
||||
foreach($this->server->getCommandMap()->getCommands() as $command){
|
||||
if(isset($commandData[$command->getLabel()]) || $command->getLabel() === "help" || !$command->testPermissionSilent($this->player)){
|
||||
continue;
|
||||
}
|
||||
|
@ -53,7 +53,6 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{
|
||||
|
||||
$compressors = [];
|
||||
|
||||
/** @var NetworkSession[][] $targetsByCompressor */
|
||||
$targetsByCompressor = [];
|
||||
foreach($recipients as $recipient){
|
||||
//TODO: different compressors might be compatible, it might not be necessary to split them up by object
|
||||
|
5
src/network/mcpe/cache/ChunkCache.php
vendored
5
src/network/mcpe/cache/ChunkCache.php
vendored
@ -78,7 +78,10 @@ class ChunkCache implements ChunkListener{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var CompressBatchPromise[] */
|
||||
/**
|
||||
* @var CompressBatchPromise[]
|
||||
* @phpstan-var array<int, CompressBatchPromise>
|
||||
*/
|
||||
private array $caches = [];
|
||||
|
||||
private int $hits = 0;
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\nbt\LittleEndianNbtSerializer;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\Tag;
|
||||
use pocketmine\nbt\TreeRoot;
|
||||
use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
use function ksort;
|
||||
use const SORT_STRING;
|
||||
@ -43,6 +44,7 @@ final class BlockStateDictionaryEntry{
|
||||
|
||||
/**
|
||||
* @param Tag[] $stateProperties
|
||||
* @phpstan-param array<string, Tag> $stateProperties
|
||||
*/
|
||||
public function __construct(
|
||||
private string $stateName,
|
||||
@ -79,6 +81,7 @@ final class BlockStateDictionaryEntry{
|
||||
|
||||
/**
|
||||
* @param Tag[] $properties
|
||||
* @phpstan-param array<string, Tag> $properties
|
||||
*/
|
||||
public static function encodeStateProperties(array $properties) : string{
|
||||
if(count($properties) === 0){
|
||||
@ -87,7 +90,7 @@ final class BlockStateDictionaryEntry{
|
||||
//TODO: make a more efficient encoding - NBT will do for now, but it's not very compact
|
||||
ksort($properties, SORT_STRING);
|
||||
$tag = new CompoundTag();
|
||||
foreach($properties as $k => $v){
|
||||
foreach(Utils::stringifyKeys($properties) as $k => $v){
|
||||
$tag->setTag($k, $v);
|
||||
}
|
||||
return (new LittleEndianNbtSerializer())->write(new TreeRoot($tag));
|
||||
|
@ -26,6 +26,7 @@ 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\Utils;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_int;
|
||||
@ -41,7 +42,7 @@ final class ItemTypeDictionaryFromDataHelper{
|
||||
}
|
||||
|
||||
$params = [];
|
||||
foreach($table as $name => $entry){
|
||||
foreach(Utils::promoteKeys($table) as $name => $entry){
|
||||
if(!is_array($entry) || !is_string($name) || !isset($entry["component_based"], $entry["runtime_id"]) || !is_bool($entry["component_based"]) || !is_int($entry["runtime_id"])){
|
||||
throw new AssumptionFailedError("Invalid item list format");
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
if(count($blockActions) > 100){
|
||||
throw new PacketHandlingException("Too many block actions in PlayerAuthInputPacket");
|
||||
}
|
||||
foreach($blockActions as $k => $blockAction){
|
||||
foreach(Utils::promoteKeys($blockActions) as $k => $blockAction){
|
||||
$actionHandled = false;
|
||||
if($blockAction instanceof PlayerBlockActionStopBreak){
|
||||
$actionHandled = $this->handlePlayerActionFromData($blockAction->getActionType(), new BlockPosition(0, 0, 0), Facing::DOWN);
|
||||
|
@ -54,6 +54,7 @@ use pocketmine\network\mcpe\protocol\types\inventory\stackresponse\ItemStackResp
|
||||
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_key_first;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
@ -370,7 +371,7 @@ class ItemStackRequestExecutor{
|
||||
* @throws ItemStackRequestProcessException
|
||||
*/
|
||||
public function generateInventoryTransaction() : InventoryTransaction{
|
||||
foreach($this->request->getActions() as $k => $action){
|
||||
foreach(Utils::promoteKeys($this->request->getActions()) as $k => $action){
|
||||
try{
|
||||
$this->processItemStackRequestAction($action);
|
||||
}catch(ItemStackRequestProcessException $e){
|
||||
|
@ -150,7 +150,7 @@ class LoginPacketHandler extends PacketHandler{
|
||||
protected function fetchAuthData(JwtChain $chain) : AuthenticationData{
|
||||
/** @var AuthenticationData|null $extraData */
|
||||
$extraData = null;
|
||||
foreach($chain->chain as $k => $jwt){
|
||||
foreach($chain->chain as $jwt){
|
||||
//validate every chain element
|
||||
try{
|
||||
[, $claims, ] = JwtUtils::parse($jwt);
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\permission;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use function fclose;
|
||||
use function fgets;
|
||||
use function fopen;
|
||||
@ -32,7 +33,10 @@ use function strtolower;
|
||||
use function trim;
|
||||
|
||||
class BanList{
|
||||
/** @var BanEntry[] */
|
||||
/**
|
||||
* @var BanEntry[]
|
||||
* @phpstan-var array<string, BanEntry>
|
||||
*/
|
||||
private array $list = [];
|
||||
|
||||
private bool $enabled = true;
|
||||
@ -101,7 +105,7 @@ class BanList{
|
||||
}
|
||||
|
||||
public function removeExpired() : void{
|
||||
foreach($this->list as $name => $entry){
|
||||
foreach(Utils::promoteKeys($this->list) as $name => $entry){
|
||||
if($entry->hasExpired()){
|
||||
unset($this->list[$name]);
|
||||
}
|
||||
|
@ -25,10 +25,14 @@ namespace pocketmine\permission;
|
||||
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\plugin\PluginException;
|
||||
use pocketmine\utils\Utils;
|
||||
use function spl_object_id;
|
||||
|
||||
class PermissionAttachment{
|
||||
/** @var bool[] */
|
||||
/**
|
||||
* @var bool[]
|
||||
* @phpstan-var array<string, bool>
|
||||
*/
|
||||
private array $permissions = [];
|
||||
|
||||
/**
|
||||
@ -60,6 +64,7 @@ class PermissionAttachment{
|
||||
|
||||
/**
|
||||
* @return bool[]
|
||||
* @phpstan-return array<string, bool>
|
||||
*/
|
||||
public function getPermissions() : array{
|
||||
return $this->permissions;
|
||||
@ -78,9 +83,10 @@ class PermissionAttachment{
|
||||
|
||||
/**
|
||||
* @param bool[] $permissions
|
||||
* @phpstan-param array<string, bool> $permissions
|
||||
*/
|
||||
public function setPermissions(array $permissions) : void{
|
||||
foreach($permissions as $key => $value){
|
||||
foreach(Utils::stringifyKeys($permissions) as $key => $value){
|
||||
$this->permissions[$key] = $value;
|
||||
}
|
||||
$this->recalculatePermissibles();
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\permission;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
|
||||
@ -37,9 +38,15 @@ class PermissionManager{
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/** @var Permission[] */
|
||||
/**
|
||||
* @var Permission[]
|
||||
* @phpstan-var array<string, Permission>
|
||||
*/
|
||||
protected array $permissions = [];
|
||||
/** @var PermissibleInternal[][] */
|
||||
/**
|
||||
* @var PermissibleInternal[][]
|
||||
* @phpstan-var array<string, array<int, PermissibleInternal>>
|
||||
*/
|
||||
protected array $permSubs = [];
|
||||
|
||||
public function getPermission(string $name) : ?Permission{
|
||||
@ -82,7 +89,7 @@ class PermissionManager{
|
||||
}
|
||||
|
||||
public function unsubscribeFromAllPermissions(PermissibleInternal $permissible) : void{
|
||||
foreach($this->permSubs as $permission => $subs){
|
||||
foreach(Utils::promoteKeys($this->permSubs) as $permission => $subs){
|
||||
if(count($subs) === 1 && isset($subs[spl_object_id($permissible)])){
|
||||
unset($this->permSubs[$permission]);
|
||||
}else{
|
||||
|
@ -70,7 +70,6 @@ final class ApiVersion{
|
||||
* @return string[]
|
||||
*/
|
||||
public static function checkAmbiguousVersions(array $versions) : array{
|
||||
/** @var VersionString[][] $indexedVersions */
|
||||
$indexedVersions = [];
|
||||
|
||||
foreach($versions as $str){
|
||||
@ -85,9 +84,8 @@ final class ApiVersion{
|
||||
}
|
||||
}
|
||||
|
||||
/** @var VersionString[] $result */
|
||||
$result = [];
|
||||
foreach($indexedVersions as $major => $list){
|
||||
foreach($indexedVersions as $list){
|
||||
if(count($list) > 1){
|
||||
array_push($result, ...$list);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\plugin;
|
||||
use pocketmine\permission\Permission;
|
||||
use pocketmine\permission\PermissionParser;
|
||||
use pocketmine\permission\PermissionParserException;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function get_debug_type;
|
||||
@ -151,7 +152,7 @@ class PluginDescription{
|
||||
$this->compatibleOperatingSystems = array_map("\strval", (array) ($plugin[self::KEY_OS] ?? []));
|
||||
|
||||
if(isset($plugin[self::KEY_COMMANDS]) && is_array($plugin[self::KEY_COMMANDS])){
|
||||
foreach($plugin[self::KEY_COMMANDS] as $commandName => $commandData){
|
||||
foreach(Utils::promoteKeys($plugin[self::KEY_COMMANDS]) as $commandName => $commandData){
|
||||
if(!is_string($commandName)){
|
||||
throw new PluginDescriptionParseException("Invalid Plugin commands, key must be the name of the command");
|
||||
}
|
||||
@ -177,7 +178,7 @@ class PluginDescription{
|
||||
if(isset($plugin[self::KEY_EXTENSIONS])){
|
||||
$extensions = (array) $plugin[self::KEY_EXTENSIONS];
|
||||
$isLinear = $extensions === array_values($extensions);
|
||||
foreach($extensions as $k => $v){
|
||||
foreach(Utils::promoteKeys($extensions) as $k => $v){
|
||||
if($isLinear){
|
||||
$k = $v;
|
||||
$v = "*";
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_flip;
|
||||
use function is_array;
|
||||
use function is_float;
|
||||
@ -77,7 +78,7 @@ class PluginGraylist{
|
||||
if(!is_array($array["plugins"])){
|
||||
throw new \InvalidArgumentException("\"plugins\" must be an array");
|
||||
}
|
||||
foreach($array["plugins"] as $k => $v){
|
||||
foreach(Utils::promoteKeys($array["plugins"]) as $k => $v){
|
||||
if(!is_string($v) && !is_int($v) && !is_float($v)){
|
||||
throw new \InvalidArgumentException("\"plugins\" contains invalid element at position $k");
|
||||
}
|
||||
|
@ -69,10 +69,16 @@ use function strtolower;
|
||||
* Manages all the plugins
|
||||
*/
|
||||
class PluginManager{
|
||||
/** @var Plugin[] */
|
||||
/**
|
||||
* @var Plugin[]
|
||||
* @phpstan-var array<string, Plugin>
|
||||
*/
|
||||
protected array $plugins = [];
|
||||
|
||||
/** @var Plugin[] */
|
||||
/**
|
||||
* @var Plugin[]
|
||||
* @phpstan-var array<string, Plugin>
|
||||
*/
|
||||
protected array $enabledPlugins = [];
|
||||
|
||||
/** @var array<string, array<string, true>> */
|
||||
@ -114,6 +120,7 @@ class PluginManager{
|
||||
|
||||
/**
|
||||
* @return Plugin[]
|
||||
* @phpstan-return array<string, Plugin>
|
||||
*/
|
||||
public function getPlugins() : array{
|
||||
return $this->plugins;
|
||||
@ -526,7 +533,7 @@ class PluginManager{
|
||||
}
|
||||
|
||||
public function tickSchedulers(int $currentTick) : void{
|
||||
foreach($this->enabledPlugins as $pluginName => $p){
|
||||
foreach(Utils::promoteKeys($this->enabledPlugins) as $pluginName => $p){
|
||||
if(isset($this->enabledPlugins[$pluginName])){
|
||||
//the plugin may have been disabled as a result of updating other plugins' schedulers, and therefore
|
||||
//removed from enabledPlugins; however, foreach will still see it due to copy-on-write
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\promise;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
|
||||
@ -84,7 +85,7 @@ final class Promise{
|
||||
$toResolve = count($promises);
|
||||
$continue = true;
|
||||
|
||||
foreach($promises as $key => $promise){
|
||||
foreach(Utils::promoteKeys($promises) as $key => $promise){
|
||||
$promise->onCompletion(
|
||||
function(mixed $value) use ($resolver, $key, $toResolve, &$values) : void{
|
||||
$values[$key] = $value;
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\resourcepacks;
|
||||
|
||||
use pocketmine\utils\Config;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\Utils;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function array_keys;
|
||||
use function copy;
|
||||
@ -87,7 +88,7 @@ class ResourcePackManager{
|
||||
throw new \InvalidArgumentException("\"resource_stack\" key should contain a list of pack names");
|
||||
}
|
||||
|
||||
foreach($resourceStack as $pos => $pack){
|
||||
foreach(Utils::promoteKeys($resourceStack) as $pos => $pack){
|
||||
if(!is_string($pack) && !is_int($pack) && !is_float($pack)){
|
||||
$logger->critical("Found invalid entry in resource pack list at offset $pos of type " . gettype($pack));
|
||||
continue;
|
||||
|
@ -403,7 +403,7 @@ final class Utils{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $trace
|
||||
* @param mixed[][] $trace
|
||||
* @return string[]
|
||||
*/
|
||||
public static function printableExceptionInfo(\Throwable $e, $trace = null) : array{
|
||||
@ -445,6 +445,7 @@ final class Utils{
|
||||
* @phpstan-param list<array<string, mixed>> $trace
|
||||
*
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public static function printableTrace(array $trace, int $maxStringLength = 80) : array{
|
||||
$messages = [];
|
||||
@ -456,7 +457,7 @@ final class Utils{
|
||||
}else{
|
||||
$args = $trace[$i]["params"];
|
||||
}
|
||||
/** @var mixed[] $args */
|
||||
/** @phpstan-var array<int, mixed> $args */
|
||||
|
||||
$paramsList = [];
|
||||
$offset = 0;
|
||||
@ -608,6 +609,18 @@ final class Utils{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets rid of PHPStan BenevolentUnionType on array keys, so that wrong type errors get reported properly
|
||||
* Use this if you don't care what the key type is and just want proper PHPStan error reporting
|
||||
*
|
||||
* @phpstan-template TValueType
|
||||
* @phpstan-param array<TValueType> $array
|
||||
* @phpstan-return array<int|string, TValueType>
|
||||
*/
|
||||
public static function promoteKeys(array $array) : array{
|
||||
return $array;
|
||||
}
|
||||
|
||||
public static function checkUTF8(string $string) : void{
|
||||
if(!mb_check_encoding($string, 'UTF-8')){
|
||||
throw new \InvalidArgumentException("Text must be valid UTF-8");
|
||||
|
@ -28,7 +28,10 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class BlockTransaction{
|
||||
/** @var Block[][][] */
|
||||
/**
|
||||
* @var Block[][][]
|
||||
* @phpstan-var array<int, array<int, array<int, Block>>>
|
||||
*/
|
||||
private array $blocks = [];
|
||||
|
||||
/**
|
||||
|
@ -56,7 +56,10 @@ use function trim;
|
||||
class WorldManager{
|
||||
public const TICKS_PER_AUTOSAVE = 300 * Server::TARGET_TICKS_PER_SECOND;
|
||||
|
||||
/** @var World[] */
|
||||
/**
|
||||
* @var World[]
|
||||
* @phpstan-var array<int, World>
|
||||
*/
|
||||
private array $worlds = [];
|
||||
private ?World $defaultWorld = null;
|
||||
|
||||
@ -76,6 +79,7 @@ class WorldManager{
|
||||
|
||||
/**
|
||||
* @return World[]
|
||||
* @phpstan-return array<int, World>
|
||||
*/
|
||||
public function getWorlds() : array{
|
||||
return $this->worlds;
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\world\format;
|
||||
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function count;
|
||||
|
||||
class SubChunk{
|
||||
@ -36,6 +35,7 @@ class SubChunk{
|
||||
* SubChunk constructor.
|
||||
*
|
||||
* @param PalettedBlockArray[] $blockLayers
|
||||
* @phpstan-param list<PalettedBlockArray> $blockLayers
|
||||
*/
|
||||
public function __construct(
|
||||
private int $emptyBlockId,
|
||||
@ -85,6 +85,7 @@ class SubChunk{
|
||||
|
||||
/**
|
||||
* @return PalettedBlockArray[]
|
||||
* @phpstan-return list<PalettedBlockArray>
|
||||
*/
|
||||
public function getBlockLayers() : array{
|
||||
return $this->blockLayers;
|
||||
@ -129,17 +130,18 @@ class SubChunk{
|
||||
}
|
||||
|
||||
public function collectGarbage() : void{
|
||||
foreach($this->blockLayers as $k => $layer){
|
||||
$cleanedLayers = [];
|
||||
foreach($this->blockLayers as $layer){
|
||||
$layer->collectGarbage();
|
||||
|
||||
foreach($layer->getPalette() as $p){
|
||||
if($p !== $this->emptyBlockId){
|
||||
$cleanedLayers[] = $layer;
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
unset($this->blockLayers[$k]);
|
||||
}
|
||||
$this->blockLayers = array_values($this->blockLayers);
|
||||
$this->blockLayers = $cleanedLayers;
|
||||
$this->biomes->collectGarbage();
|
||||
|
||||
if($this->skyLight !== null && $this->skyLight->isUniform(0)){
|
||||
|
@ -65,6 +65,8 @@ abstract class BaseWorldProvider implements WorldProvider{
|
||||
abstract protected function loadLevelData() : WorldData;
|
||||
|
||||
private function translatePalette(PalettedBlockArray $blockArray, \Logger $logger) : PalettedBlockArray{
|
||||
//TODO: missing type info in stubs
|
||||
/** @phpstan-var list<int> $palette */
|
||||
$palette = $blockArray->getPalette();
|
||||
|
||||
$newPalette = [];
|
||||
|
@ -32,6 +32,10 @@ final class ChunkData{
|
||||
* @param SubChunk[] $subChunks
|
||||
* @param CompoundTag[] $entityNBT
|
||||
* @param CompoundTag[] $tileNBT
|
||||
*
|
||||
* @phpstan-param array<int, SubChunk> $subChunks
|
||||
* @phpstan-param list<CompoundTag> $entityNBT
|
||||
* @phpstan-param list<CompoundTag> $tileNBT
|
||||
*/
|
||||
public function __construct(
|
||||
private array $subChunks,
|
||||
@ -42,14 +46,21 @@ final class ChunkData{
|
||||
|
||||
/**
|
||||
* @return SubChunk[]
|
||||
* @phpstan-return array<int, SubChunk>
|
||||
*/
|
||||
public function getSubChunks() : array{ return $this->subChunks; }
|
||||
|
||||
public function isPopulated() : bool{ return $this->populated; }
|
||||
|
||||
/** @return CompoundTag[] */
|
||||
/**
|
||||
* @return CompoundTag[]
|
||||
* @phpstan-return list<CompoundTag>
|
||||
*/
|
||||
public function getEntityNBT() : array{ return $this->entityNBT; }
|
||||
|
||||
/** @return CompoundTag[] */
|
||||
/**
|
||||
* @return CompoundTag[]
|
||||
* @phpstan-return list<CompoundTag>
|
||||
*/
|
||||
public function getTileNBT() : array{ return $this->tileNBT; }
|
||||
}
|
||||
|
@ -31,7 +31,10 @@ use const SORT_NUMERIC;
|
||||
|
||||
final class RegionGarbageMap{
|
||||
|
||||
/** @var RegionLocationTableEntry[] */
|
||||
/**
|
||||
* @var RegionLocationTableEntry[]
|
||||
* @phpstan-var array<int, RegionLocationTableEntry>
|
||||
*/
|
||||
private array $entries = [];
|
||||
private bool $clean = false;
|
||||
|
||||
@ -48,7 +51,6 @@ final class RegionGarbageMap{
|
||||
* @param RegionLocationTableEntry[]|null[] $locationTable
|
||||
*/
|
||||
public static function buildFromLocationTable(array $locationTable) : self{
|
||||
/** @var RegionLocationTableEntry[] $usedMap */
|
||||
$usedMap = [];
|
||||
foreach($locationTable as $entry){
|
||||
if($entry === null){
|
||||
@ -62,12 +64,10 @@ final class RegionGarbageMap{
|
||||
|
||||
ksort($usedMap, SORT_NUMERIC);
|
||||
|
||||
/** @var RegionLocationTableEntry[] $garbageMap */
|
||||
$garbageMap = [];
|
||||
|
||||
/** @var RegionLocationTableEntry|null $prevEntry */
|
||||
$prevEntry = null;
|
||||
foreach($usedMap as $firstSector => $entry){
|
||||
foreach($usedMap as $entry){
|
||||
$prevEndPlusOne = ($prevEntry !== null ? $prevEntry->getLastSector() + 1 : RegionLoader::FIRST_SECTOR);
|
||||
$currentStart = $entry->getFirstSector();
|
||||
if($prevEndPlusOne < $currentStart){
|
||||
|
@ -67,7 +67,10 @@ class RegionLoader{
|
||||
/** @var resource */
|
||||
protected $filePointer;
|
||||
protected int $nextSector = self::FIRST_SECTOR;
|
||||
/** @var RegionLocationTableEntry[]|null[] */
|
||||
/**
|
||||
* @var RegionLocationTableEntry[]|null[]
|
||||
* @phpstan-var list<RegionLocationTableEntry|null>
|
||||
*/
|
||||
protected array $locationTable = [];
|
||||
protected RegionGarbageMap $garbageTable;
|
||||
public int $lastUsed;
|
||||
@ -327,7 +330,6 @@ class RegionLoader{
|
||||
* @throws CorruptedRegionException
|
||||
*/
|
||||
private function checkLocationTableValidity() : void{
|
||||
/** @var int[] $usedOffsets */
|
||||
$usedOffsets = [];
|
||||
|
||||
$fileSize = filesize($this->filePath);
|
||||
@ -355,7 +357,7 @@ class RegionLoader{
|
||||
}
|
||||
ksort($usedOffsets, SORT_NUMERIC);
|
||||
$prevLocationIndex = null;
|
||||
foreach($usedOffsets as $startOffset => $locationTableIndex){
|
||||
foreach($usedOffsets as $locationTableIndex){
|
||||
if($this->locationTable[$locationTableIndex] === null){
|
||||
continue;
|
||||
}
|
||||
|
@ -72,7 +72,10 @@ abstract class RegionWorldProvider extends BaseWorldProvider{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var RegionLoader[] */
|
||||
/**
|
||||
* @var RegionLoader[]
|
||||
* @phpstan-var array<int, RegionLoader>
|
||||
*/
|
||||
protected array $regions = [];
|
||||
|
||||
protected function loadLevelData() : WorldData{
|
||||
|
@ -76,7 +76,10 @@ class PopulationTask extends AsyncTask{
|
||||
|
||||
$chunk = $this->chunk !== null ? FastChunkSerializer::deserializeTerrain($this->chunk) : null;
|
||||
|
||||
/** @var string[] $serialChunks */
|
||||
/**
|
||||
* @var string[] $serialChunks
|
||||
* @phpstan-var array<int, string|null> $serialChunks
|
||||
*/
|
||||
$serialChunks = igbinary_unserialize($this->adjacentChunks);
|
||||
$chunks = array_map(
|
||||
function(?string $serialized) : ?Chunk{
|
||||
@ -92,7 +95,6 @@ class PopulationTask extends AsyncTask{
|
||||
|
||||
self::setOrGenerateChunk($manager, $generator, $this->chunkX, $this->chunkZ, $chunk);
|
||||
|
||||
/** @var Chunk[] $resultChunks */
|
||||
$resultChunks = []; //this is just to keep phpstan's type inference happy
|
||||
foreach($chunks as $relativeChunkHash => $c){
|
||||
World::getXZ($relativeChunkHash, $relativeX, $relativeZ);
|
||||
|
@ -840,11 +840,6 @@ parameters:
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$trace of static method pocketmine\\\\utils\\\\Utils\\:\\:printableTrace\\(\\) expects array\\<int, array\\<string, mixed\\>\\>, array given\\.$#"
|
||||
count: 1
|
||||
path: ../../../src/utils/Utils.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$file of class pocketmine\\\\thread\\\\ThreadCrashInfoFrame constructor expects string\\|null, mixed given\\.$#"
|
||||
count: 1
|
||||
|
@ -110,3 +110,8 @@ parameters:
|
||||
count: 2
|
||||
path: ../../phpunit/promise/PromiseTest.php
|
||||
|
||||
-
|
||||
message: "#^Strict comparison using \\=\\=\\= between 0 and 0 will always evaluate to true\\.$#"
|
||||
count: 1
|
||||
path: ../rules/UnsafeForeachArrayOfStringRule.php
|
||||
|
||||
|
@ -28,6 +28,7 @@ use PhpParser\Node\Stmt\Foreach_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Rules\Rule;
|
||||
use PHPStan\Rules\RuleErrorBuilder;
|
||||
use PHPStan\Type\BenevolentUnionType;
|
||||
use PHPStan\Type\ClassStringType;
|
||||
use PHPStan\Type\IntegerType;
|
||||
use PHPStan\Type\StringType;
|
||||
@ -62,8 +63,17 @@ final class UnsafeForeachArrayOfStringRule implements Rule{
|
||||
|
||||
$hasCastableKeyTypes = false;
|
||||
$expectsIntKeyTypes = false;
|
||||
TypeTraverser::map($iterableType->getIterableKeyType(), function(Type $type, callable $traverse) use (&$hasCastableKeyTypes, &$expectsIntKeyTypes) : Type{
|
||||
if($type instanceof IntegerType){
|
||||
$implicitType = false;
|
||||
$benevolentUnionDepth = 0;
|
||||
TypeTraverser::map($iterableType->getIterableKeyType(), function(Type $type, callable $traverse) use (&$hasCastableKeyTypes, &$expectsIntKeyTypes, &$benevolentUnionDepth, &$implicitType) : Type{
|
||||
if($type instanceof BenevolentUnionType){
|
||||
$implicitType = true;
|
||||
$benevolentUnionDepth++;
|
||||
$result = $traverse($type);
|
||||
$benevolentUnionDepth--;
|
||||
return $result;
|
||||
}
|
||||
if($type instanceof IntegerType && $benevolentUnionDepth === 0){
|
||||
$expectsIntKeyTypes = true;
|
||||
return $type;
|
||||
}
|
||||
@ -78,12 +88,20 @@ final class UnsafeForeachArrayOfStringRule implements Rule{
|
||||
return $type;
|
||||
});
|
||||
if($hasCastableKeyTypes && !$expectsIntKeyTypes){
|
||||
$func = Utils::stringifyKeys(...);
|
||||
$tip = $implicitType ?
|
||||
sprintf(
|
||||
"Declare a key type using @phpstan-var or @phpstan-param, or use %s() to promote the key type to get proper error reporting",
|
||||
Utils::getNiceClosureName(Utils::promoteKeys(...))
|
||||
) :
|
||||
sprintf(
|
||||
"Use %s() to get a \Generator that will force the keys to string",
|
||||
Utils::getNiceClosureName(Utils::stringifyKeys(...)),
|
||||
);
|
||||
return [
|
||||
RuleErrorBuilder::message(sprintf(
|
||||
"Unsafe foreach on array with key type %s (they might be casted to int).",
|
||||
$iterableType->getIterableKeyType()->describe(VerbosityLevel::value())
|
||||
))->tip(sprintf("Use %s() for a safe Generator-based iterator.", Utils::getNiceClosureName($func)))->build()
|
||||
))->tip($tip)->build()
|
||||
];
|
||||
}
|
||||
return [];
|
||||
|
@ -135,7 +135,7 @@ class BlockTest extends TestCase{
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
foreach($expected as $typeName => $numStates){
|
||||
foreach(Utils::promoteKeys($expected) as $typeName => $numStates){
|
||||
if(!is_string($typeName) || !is_int($numStates)){
|
||||
throw new AssumptionFailedError("Old table should be array<string, int>");
|
||||
}
|
||||
|
@ -89,7 +89,6 @@ class ItemTest extends TestCase{
|
||||
}
|
||||
|
||||
public function testGetEnchantments() : void{
|
||||
/** @var EnchantmentInstance[] $enchantments */
|
||||
$enchantments = [
|
||||
new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 5),
|
||||
new EnchantmentInstance(VanillaEnchantments::SHARPNESS(), 1)
|
||||
|
@ -47,7 +47,7 @@ final class CloningRegistryTraitTest extends TestCase{
|
||||
public function testGetAllClone() : void{
|
||||
$list1 = TestCloningRegistry::getAll();
|
||||
$list2 = TestCloningRegistry::getAll();
|
||||
foreach($list1 as $k => $member){
|
||||
foreach(Utils::promoteKeys($list1) as $k => $member){
|
||||
self::assertNotSame($member, $list2[$k], "VanillaBlocks ought to clone its members");
|
||||
}
|
||||
}
|
||||
|
@ -37,9 +37,13 @@ final class TestCloningRegistry{
|
||||
|
||||
/**
|
||||
* @return \stdClass[]
|
||||
* @phpstan-return array<string, \stdClass>
|
||||
*/
|
||||
public static function getAll() : array{
|
||||
/** @var \stdClass[] $result */
|
||||
/**
|
||||
* @var \stdClass[] $result
|
||||
* @phpstan-var array<string, \stdClass> $result
|
||||
*/
|
||||
$result = self::_registryGetAll();
|
||||
return $result;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\tools\compact_regions;
|
||||
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\world\format\io\region\CorruptedRegionException;
|
||||
use pocketmine\world\format\io\region\RegionLoader;
|
||||
@ -59,6 +60,7 @@ const SUPPORTED_EXTENSIONS = [
|
||||
/**
|
||||
* @param int[] $files
|
||||
* @phpstan-param array<string, int> $files
|
||||
* @phpstan-param-out array<string, int> $files
|
||||
*/
|
||||
function find_regions_recursive(string $dir, array &$files) : void{
|
||||
$dirFiles = scandir($dir, SCANDIR_SORT_NONE);
|
||||
@ -112,7 +114,7 @@ function main(array $argv) : int{
|
||||
$corruptedFiles = [];
|
||||
$doneCount = 0;
|
||||
$totalCount = count($files);
|
||||
foreach($files as $file => $size){
|
||||
foreach(Utils::stringifyKeys($files) as $file => $size){
|
||||
try{
|
||||
$oldRegion = RegionLoader::loadExisting($file);
|
||||
}catch(CorruptedRegionException $e){
|
||||
@ -162,7 +164,7 @@ function main(array $argv) : int{
|
||||
|
||||
clearstatcache();
|
||||
$newSize = 0;
|
||||
foreach($files as $file => $oldSize){
|
||||
foreach(Utils::stringifyKeys($files) as $file => $oldSize){
|
||||
$newSize += file_exists($file) ? filesize($file) : 0;
|
||||
}
|
||||
$diff = $currentSize - $newSize;
|
||||
|
@ -198,12 +198,12 @@ class ParserPacketHandler extends PacketHandler{
|
||||
$result = (array) ($object instanceof \JsonSerializable ? $object->jsonSerialize() : $object);
|
||||
ksort($result, SORT_STRING);
|
||||
|
||||
foreach($result as $property => $value){
|
||||
foreach(Utils::promoteKeys($result) as $property => $value){
|
||||
if(is_object($value)){
|
||||
$result[$property] = self::objectToOrderedArray($value);
|
||||
}elseif(is_array($value)){
|
||||
$array = [];
|
||||
foreach($value as $k => $v){
|
||||
foreach(Utils::promoteKeys($value) as $k => $v){
|
||||
if(is_object($v)){
|
||||
$array[$k] = self::objectToOrderedArray($v);
|
||||
}else{
|
||||
@ -224,7 +224,7 @@ class ParserPacketHandler extends PacketHandler{
|
||||
}
|
||||
if(is_array($object)){
|
||||
$result = [];
|
||||
foreach($object as $k => $v){
|
||||
foreach(Utils::promoteKeys($object) as $k => $v){
|
||||
$result[$k] = self::sort($v);
|
||||
}
|
||||
return $result;
|
||||
@ -247,7 +247,7 @@ class ParserPacketHandler extends PacketHandler{
|
||||
ksort($table, SORT_STRING);
|
||||
file_put_contents($this->bedrockDataPath . '/required_item_list.json', json_encode($table, JSON_PRETTY_PRINT) . "\n");
|
||||
|
||||
foreach($packet->levelSettings->experiments->getExperiments() as $name => $experiment){
|
||||
foreach(Utils::promoteKeys($packet->levelSettings->experiments->getExperiments()) as $name => $experiment){
|
||||
echo "Experiment \"$name\" is " . ($experiment ? "" : "not ") . "active\n";
|
||||
}
|
||||
return true;
|
||||
@ -317,8 +317,8 @@ class ParserPacketHandler extends PacketHandler{
|
||||
$char = ord("A");
|
||||
|
||||
$outputsByKey = [];
|
||||
foreach($entry->getInput() as $x => $row){
|
||||
foreach($row as $y => $ingredient){
|
||||
foreach(Utils::promoteKeys($entry->getInput()) as $x => $row){
|
||||
foreach(Utils::promoteKeys($row) as $y => $ingredient){
|
||||
if($ingredient->getDescriptor() === null){
|
||||
$shape[$x][$y] = " ";
|
||||
}else{
|
||||
@ -337,7 +337,7 @@ class ParserPacketHandler extends PacketHandler{
|
||||
}
|
||||
$unlockingIngredients = $entry->getUnlockingRequirement()->getUnlockingIngredients();
|
||||
return new ShapedRecipeData(
|
||||
array_map(fn(array $array) => implode('', $array), $shape),
|
||||
array_map(fn(array $array) => implode('', array_values($array)), array_values($shape)),
|
||||
$outputsByKey,
|
||||
array_map(fn(ItemStack $output) => $this->itemStackToJson($output), $entry->getOutput()),
|
||||
$entry->getBlockName(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user