Unhackify Registry member cloning, fixes #3519

This commit is contained in:
Dylan K. Taylor 2020-05-21 11:38:02 +01:00
parent 7aca41a530
commit 8ec2ba79de
6 changed files with 158 additions and 10 deletions

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\utils\CloningRegistryTrait;
use pocketmine\utils\RegistryTrait;
use pocketmine\utils\Utils;
use function assert;
/**
@ -665,7 +665,7 @@ use function assert;
* @method static Wool YELLOW_WOOL()
*/
final class VanillaBlocks{
use RegistryTrait;
use CloningRegistryTrait;
private function __construct(){
//NOOP
@ -678,14 +678,14 @@ final class VanillaBlocks{
public static function fromString(string $name) : Block{
$result = self::_registryFromString($name);
assert($result instanceof Block);
return clone $result;
return $result;
}
/**
* @return Block[]
*/
public static function getAll() : array{
return Utils::cloneObjectArray(self::_registryGetAll());
return self::_registryGetAll();
}
protected static function setup() : void{

View File

@ -23,8 +23,8 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\utils\CloningRegistryTrait;
use pocketmine\utils\RegistryTrait;
use pocketmine\utils\Utils;
use function assert;
/**
@ -292,7 +292,7 @@ use function assert;
* @method static Skull ZOMBIE_HEAD()
*/
final class VanillaItems{
use RegistryTrait;
use CloningRegistryTrait;
private function __construct(){
//NOOP
@ -305,14 +305,14 @@ final class VanillaItems{
public static function fromString(string $name) : Item{
$result = self::_registryFromString($name);
assert($result instanceof Item);
return clone $result;
return $result;
}
/**
* @return Item[]
*/
public static function getAll() : array{
return Utils::cloneObjectArray(self::_registryGetAll());
return self::_registryGetAll();
}
protected static function setup() : void{

View File

@ -0,0 +1,32 @@
<?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;
trait CloningRegistryTrait{
use RegistryTrait;
protected static function preprocessMember(object $member) : object{
return clone $member;
}
}

View File

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace pocketmine\utils;
use function array_map;
use function count;
use function get_class;
use function implode;
@ -77,7 +78,11 @@ trait RegistryTrait{
if(!isset(self::$members[$name])){
throw new \InvalidArgumentException("No such registry member: " . self::class . "::" . $name);
}
return self::$members[$name];
return self::preprocessMember(self::$members[$name]);
}
protected static function preprocessMember(object $member) : object{
return $member;
}
/**
@ -103,7 +108,9 @@ trait RegistryTrait{
*/
private static function _registryGetAll() : array{
self::checkInit();
return self::$members;
return array_map(function(object $o) : object{
return self::preprocessMember($o);
}, self::$members);
}
/**

View File

@ -0,0 +1,55 @@
<?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;
use PHPUnit\Framework\TestCase;
use pocketmine\block\VanillaBlocks;
final class CloningRegistryTraitTest extends TestCase{
/**
* @phpstan-return \Generator<int, array{\Closure() : \stdClass}, void, void>
*/
public function cloningRegistryMembersProvider() : \Generator{
yield [function() : \stdClass{ return TestCloningRegistry::TEST1(); }];
yield [function() : \stdClass{ return TestCloningRegistry::TEST2(); }];
yield [function() : \stdClass{ return TestCloningRegistry::TEST3(); }];
}
/**
* @dataProvider cloningRegistryMembersProvider
* @phpstan-param \Closure() : \stdClass $provider
*/
public function testEachMemberClone(\Closure $provider) : void{
self::assertNotSame($provider(), $provider(), "Cloning registry should never return the same object twice");
}
public function testGetAllClone() : void{
$list1 = TestCloningRegistry::getAll();
$list2 = TestCloningRegistry::getAll();
foreach($list1 as $k => $member){
self::assertNotSame($member, $list2[$k], "VanillaBlocks ought to clone its members");
}
}
}

View File

@ -0,0 +1,54 @@
<?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;
/**
* This doc-block is generated automatically, do not modify it manually.
* This must be regenerated whenever registry members are added, removed or changed.
* @see RegistryTrait::_generateMethodAnnotations()
*
* @method static \stdClass TEST1()
* @method static \stdClass TEST2()
* @method static \stdClass TEST3()
*/
final class TestCloningRegistry{
use CloningRegistryTrait;
/**
* @return \stdClass[]
*/
public static function getAll() : array{
return self::_registryGetAll();
}
public static function fromString(string $s) : \stdClass{
return self::_registryFromString($s);
}
protected static function setup() : void{
self::_registryRegister("test1", new \stdClass());
self::_registryRegister("test2", new \stdClass());
self::_registryRegister("test3", new \stdClass());
}
}