From 71588cd8e1e53d55553efeb79e1049d3ae742519 Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sun, 29 Dec 2013 17:34:41 +0000 Subject: [PATCH 1/6] add LoadedPluginInformation class to process composer's installed.json file and pull out information about phpci plugins. --- PHPCI/Plugin/Util/LoadedPluginInformation.php | 147 ++++++++++++++++++ .../Util/LoadedPluginInformationTest.php | 51 ++++++ 2 files changed, 198 insertions(+) create mode 100644 PHPCI/Plugin/Util/LoadedPluginInformation.php create mode 100644 Tests/PHPCI/Plugin/Util/LoadedPluginInformationTest.php diff --git a/PHPCI/Plugin/Util/LoadedPluginInformation.php b/PHPCI/Plugin/Util/LoadedPluginInformation.php new file mode 100644 index 00000000..89107129 --- /dev/null +++ b/PHPCI/Plugin/Util/LoadedPluginInformation.php @@ -0,0 +1,147 @@ +composerPackages = $composerPackages; + } + + /** + * Returns an array of objects. Each one represents an available plugin + * and will have the following properties: + * name - The friendly name of the plugin (may be an empty string) + * class - The class of the plugin (will include namespace) + * @return \stdClass[] + */ + public function getInstalledPlugins() + { + $this->loadPluginInfo(); + return $this->pluginInfo; + } + + /** + * Returns an array of all the class names of plugins that have been + * loaded. + * + * @return string[] + */ + public function getPluginClasses() + { + return array_map( + function($plugin) { + return $plugin->class; + }, + $this->getInstalledPlugins() + ); + } + + protected function loadPluginInfo() + { + if ($this->pluginInfo !== null) { + return; + } + $this->pluginInfo = array(); + foreach($this->composerPackages as $package) { + $this->addPluginsFromPackage($package); + } + } + + /** + * @param \stdClass $package + */ + protected function addPluginsFromPackage($package) + { + if (isset($package->extra->phpci)) { + $phpciData = $package->extra->phpci; + + if (isset($phpciData->pluginNamespace)) { + $rootNamespace = $phpciData->pluginNamespace; + } + else { + $rootNamespace = ""; + } + + if (is_array($phpciData->suppliedPlugins)) { + $this->addPlugins( + $phpciData->suppliedPlugins, + $package->name, + $rootNamespace + ); + } + } + } + + /** + * @param \stdClass[] $plugins + * @param string $sourcePackageName + * @param string $rootNamespace + */ + protected function addPlugins( + array $plugins, + $sourcePackageName, + $rootNamespace = "") + { + foreach($plugins as $plugin) { + if (!isset($plugin->class)) { + continue; + } + $this->addPlugin($plugin, $sourcePackageName, $rootNamespace); + } + } + + /** + * @param \stdClass $plugin + * @param string $sourcePackageName + * @param string $rootNamespace + */ + protected function addPlugin( + $plugin, + $sourcePackageName, + $rootNamespace = "") + { + $newPlugin = clone $plugin; + + $newPlugin->class = $rootNamespace . $newPlugin->class; + + if (!isset($newPlugin->name)) { + $newPlugin->name = ""; + } + + $newPlugin->source = $sourcePackageName; + + $this->pluginInfo[] = $newPlugin; + } +} \ No newline at end of file diff --git a/Tests/PHPCI/Plugin/Util/LoadedPluginInformationTest.php b/Tests/PHPCI/Plugin/Util/LoadedPluginInformationTest.php new file mode 100644 index 00000000..a5c6c02f --- /dev/null +++ b/Tests/PHPCI/Plugin/Util/LoadedPluginInformationTest.php @@ -0,0 +1,51 @@ +testedInformation = LoadedPluginInformation::buildFromYaml($file); + } + + protected function phpciSetup() + { + $this->setUpFromFile( + __DIR__ . "/../../../../vendor/composer/installed.json" + ); + } + + public function testBuildFromYaml_ReturnsInstance() + { + $this->phpciSetup(); + $this->assertInstanceOf( + '\PHPCI\Plugin\Util\LoadedPluginInformation', + $this->testedInformation + ); + } + + public function testGetInstalledPlugins_ReturnsStdClassArray() + { + $this->phpciSetup(); + $plugins = $this->testedInformation->getInstalledPlugins(); + $this->assertInternalType("array", $plugins); + $this->assertContainsOnly("stdClass", $plugins); + } + + public function testGetPluginClasses_ReturnsStringArray() + { + $this->phpciSetup(); + $classes = $this->testedInformation->getPluginClasses(); + $this->assertInternalType("array", $classes); + $this->assertContainsOnly("string", $classes); + } +} + \ No newline at end of file From 4b9207e05f5826fe64b8f60a56211ad281d2c5fc Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sun, 29 Dec 2013 17:35:33 +0000 Subject: [PATCH 2/6] update plugin controller to display plugin information as well as composer package information. --- PHPCI/Controller/PluginController.php | 10 ++++++-- PHPCI/View/Plugin/index.phtml | 37 ++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index 56d01f3e..c0654a7d 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -11,6 +11,7 @@ namespace PHPCI\Controller; use b8; use PHPCI\Model\Build; +use PHPCI\Plugin\Util\LoadedPluginInformation; /** * Plugin Controller - Provides support for installing Composer packages. @@ -59,8 +60,13 @@ class PluginController extends \PHPCI\Controller $this->view->required = $this->required; $json = $this->getComposerJson(); - $this->view->installed = $json['require']; - $this->view->suggested = $json['suggest']; + $this->view->installedPackages = $json['require']; + $this->view->suggestedPackages = $json['suggest']; + + $pluginInfo = LoadedPluginInformation::buildFromYaml( + PHPCI_DIR . "vendor/composer/installed.json" + ); + $this->view->plugins = $pluginInfo->getInstalledPlugins(); return $this->view->render(); } diff --git a/PHPCI/View/Plugin/index.phtml b/PHPCI/View/Plugin/index.phtml index f9170032..d2731f2f 100644 --- a/PHPCI/View/Plugin/index.phtml +++ b/PHPCI/View/Plugin/index.phtml @@ -1,4 +1,4 @@ -

Plugins

+

Packages and Provided Plugins

PHPCI cannot automatically install/remove plugins for you, as either the shell_exec() function is disabled or PHPCI could not find Composer. PHPCI will update composer.json for you, but you will need to run Composer manually to make the changes.

@@ -21,7 +21,30 @@
-

Installed Plugins

+

Available Plugins

+ + + + + + + + + + + + + + + + + + +
NameClassProvided by Package
name; ?>class; ?>source; ?>
+
+ +
+

Installed Packages

@@ -32,7 +55,7 @@ - $version): ?> + $version): ?> @@ -48,7 +71,7 @@
-

Suggested Plugins

+

Suggested Packages

@@ -59,8 +82,8 @@ - $version): ?> - + $version): ?> + @@ -76,7 +99,7 @@
-

Search Packagist for More Plugins

+

Search Packagist for More Packages

From 6b2d0aebf1d2ec7012b5c6d51e2fbca9ce1ac152 Mon Sep 17 00:00:00 2001 From: meadsteve Date: Tue, 25 Feb 2014 19:33:00 +0000 Subject: [PATCH 3/6] rename LoadedPluginInformation to ComposerPluginInformation. --- PHPCI/Controller/PluginController.php | 4 ++-- ...inInformation.php => ComposerPluginInformation.php} | 4 ++-- ...ationTest.php => ComposerPluginInformationTest.php} | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) rename PHPCI/Plugin/Util/{LoadedPluginInformation.php => ComposerPluginInformation.php} (97%) rename Tests/PHPCI/Plugin/Util/{LoadedPluginInformationTest.php => ComposerPluginInformationTest.php} (77%) diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index c0654a7d..ad445431 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -11,7 +11,7 @@ namespace PHPCI\Controller; use b8; use PHPCI\Model\Build; -use PHPCI\Plugin\Util\LoadedPluginInformation; +use PHPCI\Plugin\Util\ComposerPluginInformation; /** * Plugin Controller - Provides support for installing Composer packages. @@ -63,7 +63,7 @@ class PluginController extends \PHPCI\Controller $this->view->installedPackages = $json['require']; $this->view->suggestedPackages = $json['suggest']; - $pluginInfo = LoadedPluginInformation::buildFromYaml( + $pluginInfo = ComposerPluginInformation::buildFromYaml( PHPCI_DIR . "vendor/composer/installed.json" ); $this->view->plugins = $pluginInfo->getInstalledPlugins(); diff --git a/PHPCI/Plugin/Util/LoadedPluginInformation.php b/PHPCI/Plugin/Util/ComposerPluginInformation.php similarity index 97% rename from PHPCI/Plugin/Util/LoadedPluginInformation.php rename to PHPCI/Plugin/Util/ComposerPluginInformation.php index 89107129..dd69a932 100644 --- a/PHPCI/Plugin/Util/LoadedPluginInformation.php +++ b/PHPCI/Plugin/Util/ComposerPluginInformation.php @@ -3,7 +3,7 @@ namespace PHPCI\Plugin\Util; -class LoadedPluginInformation +class ComposerPluginInformation { /** * @var array @@ -17,7 +17,7 @@ class LoadedPluginInformation /** * @param string $filePath The path of installed.json created by composer. - * @return LoadedPluginInformation + * @return ComposerPluginInformation */ public static function buildFromYaml($filePath) { diff --git a/Tests/PHPCI/Plugin/Util/LoadedPluginInformationTest.php b/Tests/PHPCI/Plugin/Util/ComposerPluginInformationTest.php similarity index 77% rename from Tests/PHPCI/Plugin/Util/LoadedPluginInformationTest.php rename to Tests/PHPCI/Plugin/Util/ComposerPluginInformationTest.php index a5c6c02f..113c1087 100644 --- a/Tests/PHPCI/Plugin/Util/LoadedPluginInformationTest.php +++ b/Tests/PHPCI/Plugin/Util/ComposerPluginInformationTest.php @@ -2,18 +2,18 @@ namespace PHPCI\Plugin\Tests\Util; -use PHPCI\Plugin\Util\LoadedPluginInformation; +use PHPCI\Plugin\Util\ComposerPluginInformation; -class LoadedPluginInformationTest extends \PHPUnit_Framework_TestCase +class ComposerPluginInformationTest extends \PHPUnit_Framework_TestCase { /** - * @var LoadedPluginInformation + * @var ComposerPluginInformation */ protected $testedInformation; protected function setUpFromFile($file) { - $this->testedInformation = LoadedPluginInformation::buildFromYaml($file); + $this->testedInformation = ComposerPluginInformation::buildFromYaml($file); } protected function phpciSetup() @@ -27,7 +27,7 @@ class LoadedPluginInformationTest extends \PHPUnit_Framework_TestCase { $this->phpciSetup(); $this->assertInstanceOf( - '\PHPCI\Plugin\Util\LoadedPluginInformation', + '\PHPCI\Plugin\Util\ComposerPluginInformation', $this->testedInformation ); } From dd5ff7a835e9cf383727d0fb279e69d7c8105f47 Mon Sep 17 00:00:00 2001 From: meadsteve Date: Tue, 25 Feb 2014 19:40:00 +0000 Subject: [PATCH 4/6] Extract interface for InstalledPluginInformation --- .../Plugin/Util/ComposerPluginInformation.php | 2 +- .../Util/InstalledPluginInformation.php | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 PHPCI/Plugin/Util/InstalledPluginInformation.php diff --git a/PHPCI/Plugin/Util/ComposerPluginInformation.php b/PHPCI/Plugin/Util/ComposerPluginInformation.php index dd69a932..0ce76e6f 100644 --- a/PHPCI/Plugin/Util/ComposerPluginInformation.php +++ b/PHPCI/Plugin/Util/ComposerPluginInformation.php @@ -3,7 +3,7 @@ namespace PHPCI\Plugin\Util; -class ComposerPluginInformation +class ComposerPluginInformation implements InstalledPluginInformation { /** * @var array diff --git a/PHPCI/Plugin/Util/InstalledPluginInformation.php b/PHPCI/Plugin/Util/InstalledPluginInformation.php new file mode 100644 index 00000000..f63c432a --- /dev/null +++ b/PHPCI/Plugin/Util/InstalledPluginInformation.php @@ -0,0 +1,23 @@ + Date: Tue, 25 Feb 2014 20:52:19 +0000 Subject: [PATCH 5/6] add FilesPluginInformation class to retrieve plugins contained with a directory. --- PHPCI/Plugin/Util/FilesPluginInformation.php | 104 ++++++++++++++++++ .../Util/FilesPluginInformationTest.php | 26 +++++ 2 files changed, 130 insertions(+) create mode 100644 PHPCI/Plugin/Util/FilesPluginInformation.php create mode 100644 Tests/PHPCI/Plugin/Util/FilesPluginInformationTest.php diff --git a/PHPCI/Plugin/Util/FilesPluginInformation.php b/PHPCI/Plugin/Util/FilesPluginInformation.php new file mode 100644 index 00000000..e331e722 --- /dev/null +++ b/PHPCI/Plugin/Util/FilesPluginInformation.php @@ -0,0 +1,104 @@ +files = $files; + } + + /** + * Returns an array of objects. Each one represents an available plugin + * and will have the following properties: + * name - The friendly name of the plugin (may be an empty string) + * class - The class of the plugin (will include namespace) + * @return \stdClass[] + */ + public function getInstalledPlugins() + { + if ($this->pluginInfo === null) { + $this->loadPluginInfo(); + } + return $this->pluginInfo; + } + + /** + * Returns an array of all the class names of plugins that have been + * loaded. + * + * @return string[] + */ + public function getPluginClasses() + { + return array_map( + function($plugin) { + return $plugin->class; + }, + $this->getInstalledPlugins() + ); + } + + protected function loadPluginInfo() + { + $this->pluginInfo = array(); + foreach($this->files as $fileInfo) { + if ($fileInfo instanceof \SplFileInfo) { + if ($fileInfo->isFile()) { + $this->addPluginFromFile($fileInfo); + } + } + } + } + + protected function addPluginFromFile(\SplFileInfo $fileInfo) + { + $newPlugin = new \stdClass(); + $newPlugin->class = $this->getFullClassFromFile($fileInfo); + $newPlugin->source = "core"; + $newPlugin->name = end(explode('\\', $newPlugin->class)); + + $this->pluginInfo[] = $newPlugin; + } + + protected function getFullClassFromFile(\SplFileInfo $fileInfo) + { + //TODO: Something less horrible than a regular expression + // on the conents of a file + $contents = file_get_contents($fileInfo->getRealPath()); + + $matches = array(); + preg_match('#class +([A-Za-z]+) +implements#i', $contents, $matches); + $className = $matches[1]; + + $matches = array(); + preg_match('#namespace +([A-Za-z\\\\]+);#i', $contents, $matches); + $namespace = $matches[1]; + + return $namespace . '\\' . $className; + } + +} \ No newline at end of file diff --git a/Tests/PHPCI/Plugin/Util/FilesPluginInformationTest.php b/Tests/PHPCI/Plugin/Util/FilesPluginInformationTest.php new file mode 100644 index 00000000..6e1605ab --- /dev/null +++ b/Tests/PHPCI/Plugin/Util/FilesPluginInformationTest.php @@ -0,0 +1,26 @@ +getInstalledPlugins(); + $this->assertContainsOnlyInstancesOf('stdClass', $pluginInfos); + } + + public function testGetPluginClasses_returnsStrings() + { + $pluginDirPath = realpath(__DIR__ . "/../../../../PHPCI/Plugin/"); + $test = FilesPluginInformation::newFromDir($pluginDirPath); + $pluginInfos = $test->getPluginClasses(); + $this->assertContainsOnly('string', $pluginInfos); + } +} + \ No newline at end of file From aa33a8761e9c3d318ecbf2eda83bba07f2e9f8bf Mon Sep 17 00:00:00 2001 From: meadsteve Date: Tue, 25 Feb 2014 22:22:32 +0000 Subject: [PATCH 6/6] update PluginController to use PHPCI plugin directory as well as composer to get information about installed plugins. --- PHPCI/Controller/PluginController.php | 11 ++++- PHPCI/Plugin/Util/FilesPluginInformation.php | 5 +- .../Util/PluginInformationCollection.php | 48 +++++++++++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 PHPCI/Plugin/Util/PluginInformationCollection.php diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index ad445431..b7e32ddb 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -12,6 +12,8 @@ namespace PHPCI\Controller; use b8; use PHPCI\Model\Build; use PHPCI\Plugin\Util\ComposerPluginInformation; +use PHPCI\Plugin\Util\FilesPluginInformation; +use PHPCI\Plugin\Util\PluginInformationCollection; /** * Plugin Controller - Provides support for installing Composer packages. @@ -63,9 +65,14 @@ class PluginController extends \PHPCI\Controller $this->view->installedPackages = $json['require']; $this->view->suggestedPackages = $json['suggest']; - $pluginInfo = ComposerPluginInformation::buildFromYaml( + $pluginInfo = new PluginInformationCollection(); + $pluginInfo->add(FilesPluginInformation::newFromDir( + PHPCI_DIR . "PHPCI/Plugin/" + )); + $pluginInfo->add(ComposerPluginInformation::buildFromYaml( PHPCI_DIR . "vendor/composer/installed.json" - ); + )); + $this->view->plugins = $pluginInfo->getInstalledPlugins(); return $this->view->render(); diff --git a/PHPCI/Plugin/Util/FilesPluginInformation.php b/PHPCI/Plugin/Util/FilesPluginInformation.php index e331e722..3d8b6f47 100644 --- a/PHPCI/Plugin/Util/FilesPluginInformation.php +++ b/PHPCI/Plugin/Util/FilesPluginInformation.php @@ -79,7 +79,8 @@ class FilesPluginInformation implements InstalledPluginInformation $newPlugin = new \stdClass(); $newPlugin->class = $this->getFullClassFromFile($fileInfo); $newPlugin->source = "core"; - $newPlugin->name = end(explode('\\', $newPlugin->class)); + $parts = explode('\\', $newPlugin->class); + $newPlugin->name = end($parts); $this->pluginInfo[] = $newPlugin; } @@ -87,7 +88,7 @@ class FilesPluginInformation implements InstalledPluginInformation protected function getFullClassFromFile(\SplFileInfo $fileInfo) { //TODO: Something less horrible than a regular expression - // on the conents of a file + // on the contents of a file $contents = file_get_contents($fileInfo->getRealPath()); $matches = array(); diff --git a/PHPCI/Plugin/Util/PluginInformationCollection.php b/PHPCI/Plugin/Util/PluginInformationCollection.php new file mode 100644 index 00000000..23245a8d --- /dev/null +++ b/PHPCI/Plugin/Util/PluginInformationCollection.php @@ -0,0 +1,48 @@ +pluginInformations[] = $information; + } + + /** + * Returns an array of objects. Each one represents an available plugin + * and will have the following properties: + * name - The friendly name of the plugin (may be an empty string) + * class - The class of the plugin (will include namespace) + * @return \stdClass[] + */ + public function getInstalledPlugins() + { + $arr = array(); + foreach($this->pluginInformations as $single) { + $arr = array_merge($arr, $single->getInstalledPlugins()); + } + return $arr; + } + + /** + * Returns an array of all the class names of plugins that have been + * loaded. + * + * @return string[] + */ + public function getPluginClasses() + { + $arr = array(); + foreach($this->pluginInformations as $single) { + $arr = array_merge($arr, $single->getPluginClasses()); + } + return $arr; + } + +} \ No newline at end of file