Refactor booting plugins (part 1)

This commit is contained in:
Pig Fang 2019-08-11 18:00:00 +08:00
parent a14ff87d0d
commit 2709d09823
4 changed files with 164 additions and 0 deletions

View File

@ -3,9 +3,11 @@
namespace App\Services;
use Storage;
use Exception;
use App\Events;
use Composer\Semver\Semver;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Composer\Semver\Comparator;
use Illuminate\Support\Collection;
use Illuminate\Filesystem\Filesystem;
@ -15,6 +17,11 @@ use Illuminate\Contracts\Foundation\Application;
class PluginManager
{
/**
* @var bool
*/
protected $booted = false;
/**
* @var Application
*/
@ -57,6 +64,68 @@ class PluginManager
$this->filesystem = $filesystem;
}
/**
* Boot all enabled plugins.
*/
public function boot()
{
if ($this->booted) {
return;
}
$this->enabled = collect(json_decode($this->option->get('plugins_enabled', '[]'), true));
$plugins = collect();
collect($this->filesystem->directories($this->getPluginsDir()))
->filter(function ($directory) {
return $this->filesystem->exists($directory.DIRECTORY_SEPARATOR.'package.json');
})
->each(function ($directory) use (&$plugins) {
$manifest = json_decode(
$this->filesystem->get($directory.DIRECTORY_SEPARATOR.'package.json'),
true
);
$name = $manifest['name'];
if ($plugins->has($name)) {
throw new PrettyPageException(trans('errors.plugins.duplicate', [
'dir1' => $plugins->get($name)->getPath(),
'dir2' => $directory,
]), 5);
}
$plugins->put($name, new Plugin($directory, $manifest));
});
// disable unsatisfied here
$this->registerAutoload($plugins->mapWithKeys(function ($plugin) {
return [$plugin->namespace => $plugin->getPath().'/src'];
}));
$this->booted = true;
}
/**
* @param Collection $paths
*/
protected function registerAutoload($paths)
{
spl_autoload_register(function ($class) use ($paths) {
$paths->each(function ($path, $namespace) use ($class) {
if ($namespace != '' && mb_strpos($class, $namespace) === 0) {
// Parse real file path
$path = $path.Str::replaceFirst($namespace, '', $class).'.php';
$path = str_replace('\\', '/', $path);
if ($this->filesystem->exists($path)) {
$this->filesystem->getRequire($path);
}
}
});
});
}
/**
* @return Collection
*/

View File

@ -0,0 +1,5 @@
{
"name": "fake",
"version": "0.0.0",
"namespace": "Fake"
}

View File

@ -0,0 +1,7 @@
<?php
namespace Fake;
class Faker
{
}

View File

@ -0,0 +1,83 @@
<?php
namespace Tests;
use ReflectionClass;
use App\Services\PluginManager;
use Illuminate\Filesystem\Filesystem;
class PluginManagerTest extends TestCase
{
public function rebootPluginManager(PluginManager $manager)
{
$reflection = new ReflectionClass($manager);
$property = $reflection->getProperty('booted');
$property->setAccessible(true);
$property->setValue($manager, false);
$manager->boot();
return $manager;
}
public function testPreventBootAgain()
{
// TODO: modify asserting 0 times here
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('directories')->times(1);
});
app('plugins')->boot();
app('plugins')->boot();
}
public function testRegisterAutoload()
{
config(['plugins.directory' => storage_path('mocks')]);
$this->assertFalse(class_exists('Fake\Faker'));
$manager = $this->rebootPluginManager(app('plugins'));
$this->assertTrue(class_exists('Fake\Faker'));
config(['plugins.directory' => env('PLUGINS_DIR')]);
}
public function testReportDuplicatedPlugins()
{
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('directories')
->with(config('plugins.directory'))
->once()
->andReturn(collect(['/nano', '/yuko']));
$mock->shouldReceive('exists')
->with('/nano/package.json')
->once()
->andReturn(true);
$mock->shouldReceive('get')
->with('/nano/package.json')
->once()
->andReturn(json_encode([
'name' => 'fake',
'version' => '0.0.0',
]));
$mock->shouldReceive('exists')
->with('/yuko/package.json')
->once()
->andReturn(true);
$mock->shouldReceive('get')
->with('/yuko/package.json')
->once()
->andReturn(json_encode([
'name' => 'fake',
'version' => '0.0.0',
]));
});
$this->expectExceptionMessage(trans('errors.plugins.duplicate', [
'dir1' => '/nano',
'dir2' => '/yuko',
]));
$manager = $this->rebootPluginManager(app('plugins'));
}
}