mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-10 13:35:29 +00:00
Absorb pocketmine/classloader into the core code
the only use for this class is to facilitate random runtime plugin loading, and it's not complete even for that purpose. Since nothing but PM uses pocketmine/classloader anyway, it doesn't make sense to have it outside the core. As with LogPthreads, it's just adding more maintenance work.
This commit is contained in:
parent
d2c34615f5
commit
4aba9d9725
@ -40,7 +40,6 @@
|
||||
"pocketmine/bedrock-protocol": "~21.0.0+bedrock-1.19.80",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/classloader": "dev-stable",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
"pocketmine/errorhandler": "^0.6.0",
|
||||
"pocketmine/locale-data": "~2.19.0",
|
||||
|
49
composer.lock
generated
49
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0dc836612512d87a694945ffb87a4096",
|
||||
"content-hash": "812a98ecf031488987b3216e785fee19",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -464,52 +464,6 @@
|
||||
},
|
||||
"time": "2020-12-11T01:45:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/classloader",
|
||||
"version": "dev-stable",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/ClassLoader.git",
|
||||
"reference": "e15c9b4d310581d2d2c9bf2794869cb940e011e1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/ClassLoader/zipball/e15c9b4d310581d2d2c9bf2794869cb940e011e1",
|
||||
"reference": "e15c9b4d310581d2d2c9bf2794869cb940e011e1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pmmpthread": "^6.0",
|
||||
"ext-reflection": "*",
|
||||
"php": "^8.1"
|
||||
},
|
||||
"conflict": {
|
||||
"pocketmine/spl": "<0.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "1.10.15",
|
||||
"phpstan/phpstan-strict-rules": "^1.0",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"default-branch": true,
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"./src"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-3.0"
|
||||
],
|
||||
"description": "Ad-hoc autoloading components used by PocketMine-MP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/ClassLoader/issues",
|
||||
"source": "https://github.com/pmmp/ClassLoader/tree/stable"
|
||||
},
|
||||
"time": "2023-05-19T23:39:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/color",
|
||||
"version": "0.3.1",
|
||||
@ -3252,7 +3206,6 @@
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"pocketmine/classloader": 20,
|
||||
"pocketmine/snooze": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine {
|
||||
use Composer\InstalledVersions;
|
||||
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||
use pocketmine\thread\ThreadManager;
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use pocketmine\utils\Filesystem;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\Process;
|
||||
@ -327,7 +328,7 @@ JIT_WARNING
|
||||
/*
|
||||
* We now use the Composer autoloader, but this autoloader is still for loading plugins.
|
||||
*/
|
||||
$autoloader = new \BaseClassLoader();
|
||||
$autoloader = new ThreadSafeClassLoader();
|
||||
$autoloader->register(false);
|
||||
|
||||
new Server($autoloader, $logger, $dataPath, $pluginPath);
|
||||
|
@ -93,6 +93,7 @@ use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\stats\SendUsageTask;
|
||||
use pocketmine\thread\log\AttachableThreadSafeLogger;
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\timings\TimingsHandler;
|
||||
use pocketmine\updater\UpdateChecker;
|
||||
@ -414,7 +415,7 @@ class Server{
|
||||
return $this->configGroup->getConfigString("motd", self::DEFAULT_SERVER_NAME);
|
||||
}
|
||||
|
||||
public function getLoader() : \DynamicClassLoader{
|
||||
public function getLoader() : ThreadSafeClassLoader{
|
||||
return $this->autoloader;
|
||||
}
|
||||
|
||||
@ -760,7 +761,7 @@ class Server{
|
||||
}
|
||||
|
||||
public function __construct(
|
||||
private \DynamicClassLoader $autoloader,
|
||||
private ThreadSafeClassLoader $autoloader,
|
||||
private AttachableThreadSafeLogger $logger,
|
||||
string $dataPath,
|
||||
string $pluginPath
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\plugin;
|
||||
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use function is_file;
|
||||
use function str_ends_with;
|
||||
|
||||
@ -31,7 +32,7 @@ use function str_ends_with;
|
||||
*/
|
||||
class PharPluginLoader implements PluginLoader{
|
||||
public function __construct(
|
||||
private \DynamicClassLoader $loader
|
||||
private ThreadSafeClassLoader $loader
|
||||
){}
|
||||
|
||||
public function canLoadPlugin(string $path) : bool{
|
||||
|
@ -28,6 +28,7 @@ use pmmp\thread\ThreadSafeArray;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\thread\log\ThreadSafeLogger;
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
@ -70,7 +71,7 @@ class AsyncPool{
|
||||
public function __construct(
|
||||
protected int $size,
|
||||
private int $workerMemoryLimit,
|
||||
private \ClassLoader $classLoader,
|
||||
private ThreadSafeClassLoader $classLoader,
|
||||
private ThreadSafeLogger $logger,
|
||||
private SleeperHandler $eventLoop
|
||||
){}
|
||||
|
@ -30,8 +30,8 @@ use function error_reporting;
|
||||
|
||||
trait CommonThreadPartsTrait{
|
||||
/**
|
||||
* @var ThreadSafeArray|\ClassLoader[]|null
|
||||
* @phpstan-var ThreadSafeArray<int, \ClassLoader>|null
|
||||
* @var ThreadSafeArray|ThreadSafeClassLoader[]|null
|
||||
* @phpstan-var ThreadSafeArray<int, ThreadSafeClassLoader>|null
|
||||
*/
|
||||
private ?ThreadSafeArray $classLoaders = null;
|
||||
protected ?string $composerAutoloaderPath = null;
|
||||
@ -39,14 +39,14 @@ trait CommonThreadPartsTrait{
|
||||
protected bool $isKilled = false;
|
||||
|
||||
/**
|
||||
* @return \ClassLoader[]
|
||||
* @return ThreadSafeClassLoader[]
|
||||
*/
|
||||
public function getClassLoaders() : ?array{
|
||||
return $this->classLoaders !== null ? (array) $this->classLoaders : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ClassLoader[] $autoloaders
|
||||
* @param ThreadSafeClassLoader[] $autoloaders
|
||||
*/
|
||||
public function setClassLoaders(?array $autoloaders = null) : void{
|
||||
$this->composerAutoloaderPath = \pocketmine\COMPOSER_AUTOLOADER_PATH;
|
||||
@ -82,7 +82,7 @@ trait CommonThreadPartsTrait{
|
||||
$autoloaders = $this->classLoaders;
|
||||
if($autoloaders !== null){
|
||||
foreach($autoloaders as $autoloader){
|
||||
/** @var \ClassLoader $autoloader */
|
||||
/** @var ThreadSafeClassLoader $autoloader */
|
||||
$autoloader->register(false);
|
||||
}
|
||||
}
|
||||
|
182
src/thread/ThreadSafeClassLoader.php
Normal file
182
src/thread/ThreadSafeClassLoader.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?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\thread;
|
||||
|
||||
use pmmp\thread\ThreadSafe;
|
||||
use pmmp\thread\ThreadSafeArray;
|
||||
use function class_exists;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function file_exists;
|
||||
use function interface_exists;
|
||||
use function method_exists;
|
||||
use function spl_autoload_register;
|
||||
use function str_replace;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
use function trait_exists;
|
||||
use function trim;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
/**
|
||||
* This autoloader can be used and updated from multiple threads.
|
||||
* Useful if classes need to be dynamically added after threads have already been started.
|
||||
*
|
||||
* This is used to facilitate loading plugin classes, enabling plugins to be loaded after the server has started.
|
||||
*/
|
||||
class ThreadSafeClassLoader extends ThreadSafe{
|
||||
|
||||
/**
|
||||
* @var ThreadSafeArray|string[]
|
||||
* @phpstan-var ThreadSafeArray<int, string>
|
||||
*/
|
||||
private $fallbackLookup;
|
||||
/**
|
||||
* @var ThreadSafeArray|string[][]
|
||||
* @phpstan-var ThreadSafeArray<string, ThreadSafeArray<int, string>>
|
||||
*/
|
||||
private $psr4Lookup;
|
||||
|
||||
public function __construct(){
|
||||
$this->fallbackLookup = new ThreadSafeArray();
|
||||
$this->psr4Lookup = new ThreadSafeArray();
|
||||
}
|
||||
|
||||
protected function normalizePath(string $path) : string{
|
||||
$parts = explode("://", $path, 2);
|
||||
if(count($parts) === 2){
|
||||
return $parts[0] . "://" . str_replace('/', DIRECTORY_SEPARATOR, $parts[1]);
|
||||
}
|
||||
return str_replace('/', DIRECTORY_SEPARATOR, $parts[0]);
|
||||
}
|
||||
|
||||
public function addPath(string $namespacePrefix, string $path, bool $prepend = false) : void{
|
||||
$path = $this->normalizePath($path);
|
||||
if($namespacePrefix === '' || $namespacePrefix === '\\'){
|
||||
$this->fallbackLookup->synchronized(function() use ($path, $prepend) : void{
|
||||
$this->appendOrPrependLookupEntry($this->fallbackLookup, $path, $prepend);
|
||||
});
|
||||
}else{
|
||||
$namespacePrefix = trim($namespacePrefix, '\\') . '\\';
|
||||
$this->psr4Lookup->synchronized(function() use ($namespacePrefix, $path, $prepend) : void{
|
||||
$list = $this->psr4Lookup[$namespacePrefix] ?? null;
|
||||
if($list === null){
|
||||
$list = $this->psr4Lookup[$namespacePrefix] = new ThreadSafeArray();
|
||||
}
|
||||
$this->appendOrPrependLookupEntry($list, $path, $prepend);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param ThreadSafeArray<int, string> $list
|
||||
*/
|
||||
protected function appendOrPrependLookupEntry(ThreadSafeArray $list, string $entry, bool $prepend) : void{
|
||||
if($prepend){
|
||||
$entries = $this->getAndRemoveLookupEntries($list);
|
||||
$list[] = $entry;
|
||||
foreach($entries as $removedEntry){
|
||||
$list[] = $removedEntry;
|
||||
}
|
||||
}else{
|
||||
$list[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*
|
||||
* @phpstan-param ThreadSafeArray<int, string> $list
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
protected function getAndRemoveLookupEntries(ThreadSafeArray $list) : array{
|
||||
$entries = [];
|
||||
while(($entry = $list->shift()) !== null){
|
||||
$entries[] = $entry;
|
||||
}
|
||||
return $entries;
|
||||
}
|
||||
|
||||
public function register(bool $prepend = false) : bool{
|
||||
return spl_autoload_register(function(string $name) : void{
|
||||
$this->loadClass($name);
|
||||
}, true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there is a class to load
|
||||
*/
|
||||
public function loadClass(string $name) : bool{
|
||||
$path = $this->findClass($name);
|
||||
if($path !== null){
|
||||
include($path);
|
||||
if(!class_exists($name, false) && !interface_exists($name, false) && !trait_exists($name, false)){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(method_exists($name, "onClassLoaded") && (new \ReflectionClass($name))->getMethod("onClassLoaded")->isStatic()){
|
||||
$name::onClassLoaded();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path for the class, if any
|
||||
*/
|
||||
public function findClass(string $name) : ?string{
|
||||
$baseName = str_replace("\\", DIRECTORY_SEPARATOR, $name);
|
||||
|
||||
foreach($this->fallbackLookup as $path){
|
||||
$filename = $path . DIRECTORY_SEPARATOR . $baseName . ".php";
|
||||
if(file_exists($filename)){
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = $baseName . ".php";
|
||||
|
||||
return $this->psr4Lookup->synchronized(function() use ($name, $logicalPathPsr4) : ?string{
|
||||
$subPath = $name;
|
||||
while(false !== $lastPos = strrpos($subPath, '\\')){
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
$lookup = $this->psr4Lookup[$search] ?? null;
|
||||
if($lookup !== null){
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach($lookup as $dir){
|
||||
if(file_exists($file = $dir . $pathEnd)){
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ use PHPUnit\Framework\TestCase;
|
||||
use pmmp\thread\ThreadSafeArray;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\snooze\SleeperHandler;
|
||||
use pocketmine\thread\ThreadSafeClassLoader;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use function define;
|
||||
use function dirname;
|
||||
@ -45,7 +46,7 @@ class AsyncPoolTest extends TestCase{
|
||||
public function setUp() : void{
|
||||
@define('pocketmine\\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__, 3) . '/vendor/autoload.php');
|
||||
$this->mainLogger = new MainLogger(tempnam(sys_get_temp_dir(), "pmlog"), false, "Main", new \DateTimeZone('UTC'));
|
||||
$this->pool = new AsyncPool(2, 1024, new \BaseClassLoader(), $this->mainLogger, new SleeperHandler());
|
||||
$this->pool = new AsyncPool(2, 1024, new ThreadSafeClassLoader(), $this->mainLogger, new SleeperHandler());
|
||||
}
|
||||
|
||||
public function tearDown() : void{
|
||||
|
Loading…
x
Reference in New Issue
Block a user