Refactor booting plugins (part 1)
This commit is contained in:
parent
a14ff87d0d
commit
2709d09823
@ -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
|
||||
*/
|
||||
|
5
storage/mocks/fake-plugin/package.json
Normal file
5
storage/mocks/fake-plugin/package.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "fake",
|
||||
"version": "0.0.0",
|
||||
"namespace": "Fake"
|
||||
}
|
7
storage/mocks/fake-plugin/src/Faker.php
Normal file
7
storage/mocks/fake-plugin/src/Faker.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Fake;
|
||||
|
||||
class Faker
|
||||
{
|
||||
}
|
83
tests/ServicesTest/PluginManagerTest.php
Normal file
83
tests/ServicesTest/PluginManagerTest.php
Normal 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'));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user