├── .gitignore ├── LICENSE ├── META-INF └── plugin.xml ├── PhpMetrics plugin.iml ├── PhpMetrics plugin.jar ├── README.md └── src ├── java └── hal │ └── phpmetrics │ └── idea │ ├── RunPhpMetricsAction.java │ ├── Settings.java │ ├── runner │ ├── CliRunner.java │ ├── OnEventDispatchThread.java │ └── ResultListener.java │ └── settings │ ├── SettingsForm.form │ └── SettingsForm.java └── resources └── phar └── phpmetrics.phar /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | out 3 | build.xml 4 | build.properties 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jean-François Lépine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.hal.phpmetrics.id 3 | PhpMetrics 4 | 0.1 5 | Jean-François Lépine 6 | 7 | PhpMetrics integration. 9 | 10 |

11 | PhpMetrics is a static analysis tool for PHP. 12 |

13 |

14 | Documentation available on www.phpmetrics.org website. 15 |

16 | ]]>
17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | com.intellij.modules.platform 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
-------------------------------------------------------------------------------- /PhpMetrics plugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /PhpMetrics plugin.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phpmetrics/PhpMetrics-jetbrains/1836354d183ee2a9b03ec0ab752d394eb2ffa417/PhpMetrics plugin.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PhpMetrics plugin for PhpStorm 2 | 3 | [PhpMetrics](https://github.com/Halleck45/PhpMetrics) is a static analysis tool for PHP. 4 | 5 | This (minimalist) plugin integrates PhpMetrics in your favorite IDE. 6 | 7 | ## Instruction 8 | 9 | + Download the [jar](https://github.com/Halleck45/PhpMetrics-jetbrains/raw/master/PhpMetrics%20plugin.jar). 10 | + Open `PHPStorm` > `File` > `Settings` > `Plugins` > `Install plugin from disk...` 11 | 12 | A new action `Run PhpMetrics on the selected folder` is available in the `Code` menu. 13 | 14 | # Author 15 | 16 | + Jean-François Lépine <[www.lepine.pro](http://www.lepine.pro)> 17 | 18 | # Licence 19 | 20 | See the [LICENCE](https://github.com/phpmetrics/PhpMetrics-jetbrains/blob/master/LICENSE) file 21 | -------------------------------------------------------------------------------- /src/java/hal/phpmetrics/idea/RunPhpMetricsAction.java: -------------------------------------------------------------------------------- 1 | package hal.phpmetrics.idea; 2 | 3 | import com.intellij.ide.BrowserUtil; 4 | import com.intellij.openapi.actionSystem.AnAction; 5 | import com.intellij.openapi.actionSystem.AnActionEvent; 6 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | import com.intellij.openapi.ui.MessageType; 13 | import com.intellij.openapi.ui.popup.Balloon; 14 | import com.intellij.openapi.ui.popup.JBPopupFactory; 15 | import com.intellij.openapi.wm.StatusBar; 16 | import com.intellij.openapi.wm.WindowManager; 17 | import com.intellij.ui.awt.RelativePoint; 18 | import hal.phpmetrics.idea.runner.CliRunner; 19 | import hal.phpmetrics.idea.runner.ResultListener; 20 | import hal.phpmetrics.idea.runner.OnEventDispatchThread; 21 | 22 | public class RunPhpMetricsAction extends AnAction { 23 | 24 | public void actionPerformed(final AnActionEvent e) { 25 | 26 | Project project = e.getProject(); 27 | if (project == null) { 28 | return; 29 | } 30 | 31 | VirtualFile currentDirectory = PlatformDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); 32 | if (currentDirectory == null) { 33 | inform(e, "Please select a folder of your project before running PhpMetrics", MessageType.WARNING); 34 | return; 35 | } 36 | 37 | inform(e, "PhpMetrics started. Your browser will be run in few minutes....", MessageType.INFO); 38 | 39 | try { 40 | CliRunner cliRunner = new CliRunner(Settings.getInstance(project).pathToBinary); 41 | final File destination = File.createTempFile("phpmetrics-idea", ".html"); 42 | String[] command = new String[]{"--report-html=" + destination, currentDirectory.getPath()}; 43 | 44 | cliRunner.run(command, new OnEventDispatchThread(new ResultListener() { 45 | @Override 46 | public void onSuccess(String output) { 47 | BrowserUtil.browse(destination); 48 | } 49 | 50 | @Override 51 | public void onError(String error, String output, int exitCode) { 52 | inform(e, "An error occurred. Please verify your PhpMetrics installation\n" + output, MessageType.ERROR); 53 | } 54 | })); 55 | } catch (IOException e1) { 56 | e1.printStackTrace(); 57 | } 58 | } 59 | 60 | private void inform(AnActionEvent e, String text, MessageType messageType) { 61 | StatusBar statusBar = WindowManager.getInstance() 62 | .getStatusBar(PlatformDataKeys.PROJECT.getData(e.getDataContext())); 63 | JBPopupFactory.getInstance() 64 | .createHtmlTextBalloonBuilder(text, messageType, null) 65 | .setFadeoutTime(7500) 66 | .createBalloon() 67 | .show(RelativePoint.getCenterOf(statusBar.getComponent()), 68 | Balloon.Position.atRight); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/java/hal/phpmetrics/idea/Settings.java: -------------------------------------------------------------------------------- 1 | package hal.phpmetrics.idea; 2 | 3 | import com.intellij.openapi.components.*; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.util.xmlb.XmlSerializerUtil; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | @State( 9 | name = "PhpMetricsPluginSettings", 10 | storages = { 11 | @Storage(id = "default", file = StoragePathMacros.PROJECT_FILE), 12 | @Storage(id = "dir", file = StoragePathMacros.PROJECT_CONFIG_DIR + "/phpmetrics.xml", scheme = StorageScheme.DIRECTORY_BASED) 13 | } 14 | ) 15 | public class Settings implements PersistentStateComponent { 16 | 17 | public String pathToBinary; 18 | 19 | @Nullable 20 | @Override 21 | public Settings getState() { 22 | return this; 23 | } 24 | 25 | @Override 26 | public void loadState(Settings settings) { 27 | XmlSerializerUtil.copyBean(settings, this); 28 | } 29 | 30 | public static Settings getInstance(Project project) { 31 | return ServiceManager.getService(project, Settings.class); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/java/hal/phpmetrics/idea/runner/CliRunner.java: -------------------------------------------------------------------------------- 1 | package hal.phpmetrics.idea.runner; 2 | 3 | import java.io.*; 4 | import java.net.URL; 5 | 6 | public class CliRunner { 7 | 8 | private String binPath; 9 | 10 | private static String resultFromStream(InputStream stream) { 11 | try { 12 | String processResult = ""; 13 | String line; 14 | BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); 15 | while ((line = reader.readLine()) != null) { 16 | processResult += line; 17 | } 18 | reader.close(); 19 | return processResult; 20 | } catch (IOException exception) { 21 | exception.printStackTrace(); 22 | } 23 | return ""; 24 | } 25 | 26 | 27 | public static CliRunner withIncludedPhar() { 28 | return new CliRunner(null); 29 | } 30 | 31 | public CliRunner(String binPath) { 32 | this.binPath = binPath; 33 | } 34 | 35 | public void run(String[] arguments, final ResultListener listener) { 36 | final String[] command = new String[arguments.length + 2]; 37 | command[0] = "php"; 38 | command[1] = binPath != null ? binPath : getExternalPath("/phar/phpmetrics.phar"); 39 | System.arraycopy(arguments, 0, command, 2, arguments.length); 40 | 41 | new Thread() { 42 | @Override 43 | public void run() { 44 | try { 45 | final Process process = Runtime.getRuntime().exec(command); 46 | process.waitFor(); 47 | 48 | if (process.exitValue() == 0) { 49 | listener.onSuccess(resultFromStream(process.getInputStream())); 50 | return; 51 | } 52 | 53 | listener.onError(resultFromStream(process.getErrorStream()), resultFromStream(process.getInputStream()), process.exitValue()); 54 | } catch (IOException exception) { 55 | exception.printStackTrace(); 56 | } catch (InterruptedException exception) { 57 | exception.printStackTrace(); 58 | } 59 | } 60 | }.start(); 61 | } 62 | 63 | private String getExternalPath(String resource) throws NullPointerException { 64 | File file = null; 65 | URL res = getClass().getResource(resource); 66 | if (res.toString().startsWith("jar:") ||res.toString().contains(".jar!")) { 67 | try { 68 | InputStream input = getClass().getResourceAsStream(resource); 69 | file = File.createTempFile("tempfile", ".tmp"); 70 | OutputStream out = new FileOutputStream(file); 71 | int read; 72 | byte[] bytes = new byte[1024]; 73 | 74 | while ((read = input.read(bytes)) != -1) { 75 | out.write(bytes, 0, read); 76 | } 77 | file.deleteOnExit(); 78 | } catch (IOException ex) { 79 | ex.printStackTrace(); 80 | } 81 | } else { 82 | file = new File(res.getFile().replace("%20", "\\ ")); 83 | 84 | if (!file.exists()) { 85 | file = new File(res.getFile().replace("%20", " ")); 86 | } 87 | } 88 | 89 | if (file != null && ! file.exists()) { 90 | throw new RuntimeException("Error: File " + file + " not found!"); 91 | } 92 | if (file == null) { 93 | throw new RuntimeException("Error: Could not found the phpmetrics bin file"); 94 | } 95 | 96 | return file.getAbsolutePath(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/java/hal/phpmetrics/idea/runner/OnEventDispatchThread.java: -------------------------------------------------------------------------------- 1 | package hal.phpmetrics.idea.runner; 2 | 3 | import com.intellij.openapi.application.Application; 4 | import com.intellij.openapi.application.ApplicationManager; 5 | import com.intellij.openapi.application.ModalityState; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.awt.*; 9 | 10 | /** 11 | * Decorator that allows to to run the listener from the event dispatch thread. 12 | * 13 | * It is just a convenience class. 14 | * See http://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/general_threading_rules.html for more 15 | * information. 16 | */ 17 | public class OnEventDispatchThread implements ResultListener { 18 | private Component modifiedComponent; 19 | private ResultListener decorated; 20 | 21 | public OnEventDispatchThread(@NotNull ResultListener decorated, @NotNull Component target) { 22 | this.modifiedComponent = target; 23 | this.decorated = decorated; 24 | } 25 | 26 | public OnEventDispatchThread(@NotNull ResultListener decorated) { 27 | this.decorated = decorated; 28 | } 29 | 30 | @Override 31 | public void onSuccess(final String output) { 32 | final Application application = ApplicationManager.getApplication(); 33 | application.invokeLater(new Runnable() { 34 | @Override 35 | public void run() { 36 | decorated.onSuccess(output); 37 | } 38 | }, modalityStateFor(modifiedComponent)); 39 | } 40 | 41 | @Override 42 | public void onError(final String error, final String output, final int exitCode) { 43 | final Application application = ApplicationManager.getApplication(); 44 | application.invokeLater(new Runnable() { 45 | @Override 46 | public void run() { 47 | decorated.onError(error, output, exitCode); 48 | } 49 | }, modalityStateFor(modifiedComponent)); 50 | } 51 | 52 | private ModalityState modalityStateFor(Component component) { 53 | return component != null 54 | ? ModalityState.stateForComponent(component) 55 | : ModalityState.any(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/java/hal/phpmetrics/idea/runner/ResultListener.java: -------------------------------------------------------------------------------- 1 | package hal.phpmetrics.idea.runner; 2 | 3 | public interface ResultListener { 4 | void onSuccess(String output); 5 | void onError(String error, String output, int exitCode); 6 | } 7 | -------------------------------------------------------------------------------- /src/java/hal/phpmetrics/idea/settings/SettingsForm.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 | -------------------------------------------------------------------------------- /src/java/hal/phpmetrics/idea/settings/SettingsForm.java: -------------------------------------------------------------------------------- 1 | package hal.phpmetrics.idea.settings; 2 | 3 | import com.intellij.openapi.fileChooser.FileChooserDescriptor; 4 | import com.intellij.openapi.options.Configurable; 5 | import com.intellij.openapi.options.ConfigurationException; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.openapi.ui.MessageType; 8 | import com.intellij.openapi.ui.TextBrowseFolderListener; 9 | import com.intellij.openapi.ui.TextFieldWithBrowseButton; 10 | import com.intellij.openapi.ui.popup.Balloon; 11 | import com.intellij.openapi.ui.popup.JBPopupFactory; 12 | import com.intellij.ui.awt.RelativePoint; 13 | import hal.phpmetrics.idea.Settings; 14 | import hal.phpmetrics.idea.runner.CliRunner; 15 | import hal.phpmetrics.idea.runner.ResultListener; 16 | import hal.phpmetrics.idea.runner.OnEventDispatchThread; 17 | import org.jetbrains.annotations.Nls; 18 | import org.jetbrains.annotations.NotNull; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import javax.swing.*; 22 | import java.awt.event.*; 23 | 24 | public class SettingsForm implements Configurable { 25 | 26 | private Project project; 27 | 28 | private JPanel settingsPanel; 29 | private TextFieldWithBrowseButton customPath; 30 | private JLabel includedVersion; 31 | private JLabel customVersion; 32 | private JButton useIncludedButton; 33 | 34 | public SettingsForm(@NotNull final Project project) { 35 | this.project = project; 36 | } 37 | 38 | @Nls 39 | @Override 40 | public String getDisplayName() { 41 | return "PhpMetrics"; 42 | } 43 | 44 | @Nullable 45 | @Override 46 | public String getHelpTopic() { 47 | return null; 48 | } 49 | 50 | @Nullable 51 | @Override 52 | public JComponent createComponent() { 53 | loadIncludedVersion(); 54 | loadCustomVersion(); 55 | 56 | FileChooserDescriptor binaryDescriptor = new FileChooserDescriptor(true, false, false, false, false, false); 57 | binaryDescriptor.setTitle("Choose PhpMetrics Installation"); 58 | customPath.addBrowseFolderListener(new TextBrowseFolderListener(new FileChooserDescriptor(true, false, false, false, false, false))); 59 | customPath.addActionListener(new ActionListener() { 60 | @Override 61 | public void actionPerformed(ActionEvent actionEvent) { 62 | new CliRunner(customPath.getText()).run(new String[]{"--version"}, new OnEventDispatchThread(new ResultListener() { 63 | @Override 64 | public void onSuccess(String output) { 65 | if (isValidInstallation(output)) { 66 | setCustomVersion(extractVersion(output)); 67 | inform(customVersion, "Seems to be a valid PhpMetrics installation :-)", MessageType.INFO); 68 | return; 69 | } 70 | 71 | unsetCustomVersion(); 72 | inform(customPath, "Seems to be no PhpMetrics installation :-(", MessageType.ERROR); 73 | } 74 | 75 | @Override 76 | public void onError(String error, String output, int exitCode) { 77 | unsetCustomVersion(); 78 | inform(customPath, "Seems to be no PhpMetrics installation :-(", MessageType.ERROR); 79 | } 80 | }, customVersion)); 81 | } 82 | }); 83 | 84 | useIncludedButton.addActionListener(new ActionListener() { 85 | @Override 86 | public void actionPerformed(ActionEvent actionEvent) { 87 | unsetCustomVersion(); 88 | } 89 | }); 90 | 91 | return settingsPanel; 92 | } 93 | 94 | @Override 95 | public void disposeUIResources() { 96 | } 97 | 98 | @Override 99 | public boolean isModified() { 100 | return ! customPath.getText().equals(getSettings().pathToBinary); 101 | } 102 | 103 | @Override 104 | public void apply() throws ConfigurationException { 105 | String pathToBinary = null; 106 | if (customPath.getText().length() > 0) { 107 | pathToBinary = customPath.getText(); 108 | } 109 | getSettings().pathToBinary = pathToBinary; 110 | } 111 | 112 | @Override 113 | public void reset() { 114 | if (getSettings().pathToBinary == null) { 115 | unsetCustomVersion(); 116 | return; 117 | } 118 | customPath.setText(getSettings().pathToBinary); 119 | loadCustomVersion(); 120 | } 121 | 122 | private void loadCustomVersion() { 123 | new CliRunner(getSettings().pathToBinary).run(new String[]{"--version"}, new OnEventDispatchThread(new ResultListener() { 124 | @Override 125 | public void onSuccess(String output) { 126 | if (isValidInstallation(output)) { 127 | setCustomVersion(extractVersion(output)); 128 | return; 129 | } 130 | unsetCustomVersion(); 131 | } 132 | 133 | @Override 134 | public void onError(String error, String output, int exitCode) { 135 | unsetCustomVersion(); 136 | } 137 | }, customVersion)); 138 | } 139 | 140 | private void loadIncludedVersion() { 141 | CliRunner.withIncludedPhar().run(new String[]{"--version"}, new OnEventDispatchThread(new ResultListener() { 142 | @Override 143 | public void onSuccess(String output) { 144 | includedVersion.setText(extractVersion(output)); 145 | } 146 | 147 | @Override 148 | public void onError(String error, String output, int exitCode) { 149 | } 150 | }, includedVersion)); 151 | } 152 | private boolean isValidInstallation(@NotNull String output) { 153 | return output.contains("PhpMetrics") 154 | && ! extractVersion(output).isEmpty(); 155 | } 156 | 157 | private String extractVersion(@NotNull String versionText) { 158 | String version = versionText.replaceFirst("^.* version v?", ""); 159 | if (version.equals(versionText)) { 160 | return ""; 161 | } 162 | return version; 163 | } 164 | 165 | private void setCustomVersion(String version) { 166 | customVersion.setText(version); 167 | useIncludedButton.setEnabled(true); 168 | } 169 | 170 | private void unsetCustomVersion() { 171 | customPath.setText(""); 172 | customVersion.setText(""); 173 | useIncludedButton.setEnabled(false); 174 | } 175 | 176 | private void inform(JComponent target, String text, MessageType messageType) { 177 | JBPopupFactory.getInstance() 178 | .createHtmlTextBalloonBuilder(text, messageType, null) 179 | .setFadeoutTime(2000) 180 | .createBalloon() 181 | .show(RelativePoint.getNorthEastOf(target), Balloon.Position.atRight); 182 | } 183 | 184 | private Settings getSettings() { 185 | return Settings.getInstance(project); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/resources/phar/phpmetrics.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phpmetrics/PhpMetrics-jetbrains/1836354d183ee2a9b03ec0ab752d394eb2ffa417/src/resources/phar/phpmetrics.phar --------------------------------------------------------------------------------