From 18019d85e68ad86da6fcfcabe41a17723b08b7be Mon Sep 17 00:00:00 2001 From: Pig Fang Date: Mon, 12 Aug 2019 14:35:36 +0800 Subject: [PATCH] Refactor plugin system (part 3) --- app/Providers/PluginServiceProvider.php | 6 +- app/Services/Plugin.php | 2 +- app/Services/PluginManager.php | 61 +++++++++- tests/ServicesTest/PluginManagerTest.php | 136 +++++++++++++++++++++++ 4 files changed, 198 insertions(+), 7 deletions(-) diff --git a/app/Providers/PluginServiceProvider.php b/app/Providers/PluginServiceProvider.php index b639658a..36226cd5 100644 --- a/app/Providers/PluginServiceProvider.php +++ b/app/Providers/PluginServiceProvider.php @@ -40,13 +40,13 @@ class PluginServiceProvider extends ServiceProvider foreach ($plugins->getPlugins() as $plugin) { if ($plugin->isEnabled()) { - $src_paths[$plugin->getNameSpace()] = $plugin->getPath().'/src'; + $src_paths[$plugin->getNamespace()] = $plugin->getPath().'/src'; // Add paths of views - $finder->addNamespace($plugin->getNameSpace(), $plugin->getPath().'/views'); + $finder->addNamespace($plugin->getNamespace(), $plugin->getPath().'/views'); } // Always add paths of translation files for namespace hints - $loader->addNamespace($plugin->getNameSpace(), $plugin->getPath().'/lang'); + $loader->addNamespace($plugin->getNamespace(), $plugin->getPath().'/lang'); } $this->registerPluginCallbackListener(); diff --git a/app/Services/Plugin.php b/app/Services/Plugin.php index c986f055..4c3ddefe 100644 --- a/app/Services/Plugin.php +++ b/app/Services/Plugin.php @@ -111,7 +111,7 @@ class Plugin return $this; } - public function getNameSpace(): string + public function getNamespace(): string { return $this->namespace; } diff --git a/app/Services/PluginManager.php b/app/Services/PluginManager.php index 8a7f886c..2be38fc5 100644 --- a/app/Services/PluginManager.php +++ b/app/Services/PluginManager.php @@ -103,18 +103,25 @@ class PluginManager // disable unsatisfied here + $enabled = $plugins->filter(function ($plugin) { + return $plugin->isEnabled(); + }); + $this->registerAutoload( - $plugins->filter(function ($plugin) { - return $plugin->isEnabled(); - })->mapWithKeys(function ($plugin) { + $enabled->mapWithKeys(function ($plugin) { return [$plugin->namespace => $plugin->getPath().'/src']; }) ); + $this->loadVendor($enabled); + $this->loadViewsAndTranslations($enabled); + $this->loadBootstrapper($enabled); $this->booted = true; } /** + * Register classes autoloading. + * * @param Collection $paths */ protected function registerAutoload($paths) @@ -134,6 +141,54 @@ class PluginManager }); } + /** + * Load Composer dumped autoload file. + * + * @param Collection $enabled + */ + protected function loadVendor($enabled) + { + $enabled->each(function ($plugin) { + $path = $plugin->getPath().'/vendor/autoload.php'; + if ($this->filesystem->exists($path)) { + $this->filesystem->getRequire($path); + } + }); + } + + /** + * Load views and translations. + * + * @param Collection $enabled + */ + protected function loadViewsAndTranslations($enabled) + { + $translations = $this->app->make('translation.loader'); + $view = $this->app->make('view'); + $enabled->each(function ($plugin) use (&$translations, &$view) { + $namespace = $plugin->namespace; + $path = $plugin->getPath(); + + $translations->addNamespace($namespace, $path.'/lang'); + $view->addNamespace($namespace, $path.'/views'); + }); + } + + /** + * Load plugin's bootstrapper. + * + * @param Collection $enabled + */ + protected function loadBootstrapper($enabled) + { + $enabled->each(function ($plugin) { + $path = $plugin->getPath().'/bootstrap.php'; + if ($this->filesystem->exists($path)) { + $this->app->call($this->filesystem->getRequire($path)); + } + }); + } + /** * @return Collection */ diff --git a/tests/ServicesTest/PluginManagerTest.php b/tests/ServicesTest/PluginManagerTest.php index 3d725986..7c9d43d8 100644 --- a/tests/ServicesTest/PluginManagerTest.php +++ b/tests/ServicesTest/PluginManagerTest.php @@ -82,6 +82,142 @@ class PluginManagerTest extends TestCase $manager = $this->rebootPluginManager(app('plugins')); } + public function testLoadComposer() + { + option(['plugins_enabled' => json_encode([['name' => 'mayaka', 'version' => '0.0.0']])]); + $this->mock(Filesystem::class, function ($mock) { + $mock->shouldReceive('directories') + ->with(base_path('plugins')) + ->once() + ->andReturn(collect(['/mayaka'])); + + $mock->shouldReceive('exists') + ->with('/mayaka'.DIRECTORY_SEPARATOR.'package.json') + ->once() + ->andReturn(true); + + $mock->shouldReceive('get') + ->with('/mayaka'.DIRECTORY_SEPARATOR.'package.json') + ->once() + ->andReturn(json_encode([ + 'name' => 'mayaka', + 'version' => '0.0.0', + ])); + + $mock->shouldReceive('exists') + ->with('/mayaka/vendor/autoload.php') + ->once() + ->andReturn(true); + + $mock->shouldReceive('getRequire') + ->with('/mayaka/vendor/autoload.php') + ->once(); + + $mock->shouldReceive('exists') + ->with('/mayaka/bootstrap.php') + ->once() + ->andReturn(false); + }); + + $manager = $this->rebootPluginManager(app('plugins')); + + option(['plugins_enabled' => '[]']); + } + + public function testLoadViewsAndTranslations() + { + option(['plugins_enabled' => json_encode([['name' => 'mayaka', 'version' => '0.0.0']])]); + $this->mock(Filesystem::class, function ($mock) { + $mock->shouldReceive('directories') + ->with(base_path('plugins')) + ->once() + ->andReturn(collect(['/mayaka'])); + + $mock->shouldReceive('exists') + ->with('/mayaka'.DIRECTORY_SEPARATOR.'package.json') + ->once() + ->andReturn(true); + + $mock->shouldReceive('get') + ->with('/mayaka'.DIRECTORY_SEPARATOR.'package.json') + ->once() + ->andReturn(json_encode([ + 'name' => 'mayaka', + 'version' => '0.0.0', + 'namespace' => 'Mayaka', + ])); + + $mock->shouldReceive('exists') + ->with('/mayaka/vendor/autoload.php') + ->once() + ->andReturn(false); + + $mock->shouldReceive('exists') + ->with('/mayaka/bootstrap.php') + ->once() + ->andReturn(false); + }); + $this->mock('view', function ($mock) { + $mock->shouldReceive('addNamespace') + ->withArgs(['Mayaka', '/mayaka/views']) + ->once(); + }); + $this->instance('translation.loader', \Mockery::mock(\App\Services\TranslationLoader::class, function ($mock) { + $mock->shouldReceive('addNamespace') + ->withArgs(['Mayaka', '/mayaka/lang']) + ->once(); + })); + + $manager = $this->rebootPluginManager(app('plugins')); + + option(['plugins_enabled' => '[]']); + } + + public function testLoadBootstrapper() + { + option(['plugins_enabled' => json_encode([['name' => 'mayaka', 'version' => '0.0.0']])]); + $this->mock(Filesystem::class, function ($mock) { + $mock->shouldReceive('directories') + ->with(base_path('plugins')) + ->once() + ->andReturn(collect(['/mayaka'])); + + $mock->shouldReceive('exists') + ->with('/mayaka'.DIRECTORY_SEPARATOR.'package.json') + ->once() + ->andReturn(true); + + $mock->shouldReceive('get') + ->with('/mayaka'.DIRECTORY_SEPARATOR.'package.json') + ->once() + ->andReturn(json_encode([ + 'name' => 'mayaka', + 'version' => '0.0.0', + ])); + + $mock->shouldReceive('exists') + ->with('/mayaka/vendor/autoload.php') + ->once() + ->andReturn(false); + + $mock->shouldReceive('exists') + ->with('/mayaka/bootstrap.php') + ->once() + ->andReturn(true); + + $mock->shouldReceive('getRequire') + ->with('/mayaka/bootstrap.php') + ->once() + ->andReturn(function (\Illuminate\Contracts\Events\Dispatcher $events) { + $this->assertTrue(method_exists($events, 'listen')); + }); + }); + + $manager = $this->rebootPluginManager(app('plugins')); + + option(['plugins_enabled' => '[]']); + } + public function testRegisterAutoload() { $dir = config('plugins.directory');