diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index 56d01f3e..b7e32ddb 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -11,6 +11,9 @@ 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. @@ -59,8 +62,18 @@ 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 = 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/Helper/BuildInterpolator.php b/PHPCI/Helper/BuildInterpolator.php index 4732607c..d4609793 100644 --- a/PHPCI/Helper/BuildInterpolator.php +++ b/PHPCI/Helper/BuildInterpolator.php @@ -27,7 +27,7 @@ class BuildInterpolator $this->interpolation_vars['%COMMIT%'] = $build->getCommitId(); $this->interpolation_vars['%PROJECT%'] = $build->getProjectId(); $this->interpolation_vars['%BUILD%'] = $build->getId(); - $this->interpolation_vars['%PROJECT_TITLE%'] = $build->getProject()->getTitle(); + $this->interpolation_vars['%PROJECT_TITLE%'] = $build->getProjectTitle(); $this->interpolation_vars['%BUILD_PATH%'] = $buildPath; $this->interpolation_vars['%BUILD_URI%'] = $phpCiUrl . "build/view/" . $build->getId(); $this->interpolation_vars['%PHPCI_COMMIT%'] = $this->interpolation_vars['%COMMIT%']; diff --git a/PHPCI/Model/Build.php b/PHPCI/Model/Build.php index d61fe832..0d9b64fb 100644 --- a/PHPCI/Model/Build.php +++ b/PHPCI/Model/Build.php @@ -36,6 +36,15 @@ class Build extends BuildBase return '#'; } + /** + * @return string + */ + public function getProjectTitle() + { + $project = $this->getProject(); + return $project ? $project->getTitle() : ""; + } + /** * Get link to branch from another source (i.e. Github) */ diff --git a/PHPCI/Plugin/Email.php b/PHPCI/Plugin/Email.php index 05dbe4c0..5d29de65 100644 --- a/PHPCI/Plugin/Email.php +++ b/PHPCI/Plugin/Email.php @@ -106,9 +106,10 @@ class Email implements \PHPCI\Plugin } /** - * @param array|string $toAddresses Array or single address to send to - * @param string $subject Email subject - * @param string $body Email body + * @param string[]|string $toAddresses Array or single address to send to + * @param string[] $ccList + * @param string $subject Email subject + * @param string $body Email body * @return array Array of failed addresses */ public function sendEmail($toAddresses, $ccList, $subject, $body) diff --git a/PHPCI/Plugin/Util/ComposerPluginInformation.php b/PHPCI/Plugin/Util/ComposerPluginInformation.php new file mode 100644 index 00000000..0ce76e6f --- /dev/null +++ b/PHPCI/Plugin/Util/ComposerPluginInformation.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/PHPCI/Plugin/Util/FilesPluginInformation.php b/PHPCI/Plugin/Util/FilesPluginInformation.php new file mode 100644 index 00000000..3d8b6f47 --- /dev/null +++ b/PHPCI/Plugin/Util/FilesPluginInformation.php @@ -0,0 +1,105 @@ +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"; + $parts = explode('\\', $newPlugin->class); + $newPlugin->name = end($parts); + + $this->pluginInfo[] = $newPlugin; + } + + protected function getFullClassFromFile(\SplFileInfo $fileInfo) + { + //TODO: Something less horrible than a regular expression + // on the contents 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/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 @@ +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 diff --git a/PHPCI/View/Plugin/index.phtml b/PHPCI/View/Plugin/index.phtml index 134cf260..3cfac153 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

diff --git a/Tests/PHPCI/Plugin/EmailTest.php b/Tests/PHPCI/Plugin/EmailTest.php index 84618001..87225772 100644 --- a/Tests/PHPCI/Plugin/EmailTest.php +++ b/Tests/PHPCI/Plugin/EmailTest.php @@ -162,7 +162,7 @@ class EmailTest extends \PHPUnit_Framework_TestCase { $this->mockMailer->expects($this->once()) ->method('send'); - $this->testedEmailPlugin->sendEmail("test@email.com", "hello", "body"); + $this->testedEmailPlugin->sendEmail("test@email.com", array(), "hello", "body"); } /** @@ -177,7 +177,7 @@ class EmailTest extends \PHPUnit_Framework_TestCase $this->mockMailer->expects($this->once()) ->method('send') ->with($this->isInstanceOf('\Swift_Message'), $this->anything()); - $this->testedEmailPlugin->sendEmail($toAddress, $subject, $body); + $this->testedEmailPlugin->sendEmail($toAddress, array(), $subject, $body); } /** @@ -197,7 +197,7 @@ class EmailTest extends \PHPUnit_Framework_TestCase $actualMail = null; $this->catchMailPassedToSend($actualMail); - $this->testedEmailPlugin->sendEmail($toAddress, $subject, $body); + $this->testedEmailPlugin->sendEmail($toAddress, array(), $subject, $body); $this->assertSystemMail( $toAddress, diff --git a/Tests/PHPCI/Plugin/Util/ComposerPluginInformationTest.php b/Tests/PHPCI/Plugin/Util/ComposerPluginInformationTest.php new file mode 100644 index 00000000..113c1087 --- /dev/null +++ b/Tests/PHPCI/Plugin/Util/ComposerPluginInformationTest.php @@ -0,0 +1,51 @@ +testedInformation = ComposerPluginInformation::buildFromYaml($file); + } + + protected function phpciSetup() + { + $this->setUpFromFile( + __DIR__ . "/../../../../vendor/composer/installed.json" + ); + } + + public function testBuildFromYaml_ReturnsInstance() + { + $this->phpciSetup(); + $this->assertInstanceOf( + '\PHPCI\Plugin\Util\ComposerPluginInformation', + $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 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 diff --git a/phpci.yml b/phpci.yml index 4409d938..8d962dca 100644 --- a/phpci.yml +++ b/phpci.yml @@ -8,11 +8,13 @@ build_settings: test: php_mess_detector: + allow_failures: true # Temporarily allowing failures, remove this! php_code_sniffer: + allow_failures: true # Temporarily allowing failures, remove this! standard: "PSR2" php_loc: failure: email: committer: true - cc: ["php-ci@googlegroups.com"] \ No newline at end of file + cc: ["php-ci@googlegroups.com"]