/******************************************************************************* * Copyright (c) 2015 Zend Technologies and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Zend Technologies - initial API and implementation *******************************************************************************/ package org.eclipse.php.internal.debug.core.debugger; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.*; import org.eclipse.debug.core.*; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.LaunchConfigurationDelegate; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.php.debug.core.debugger.launching.ILaunchDelegateListener; import org.eclipse.php.debug.core.debugger.parameters.IDebugParametersKeys; import org.eclipse.php.internal.debug.core.*; import org.eclipse.php.internal.debug.core.launching.PHPLaunchUtilities; import org.eclipse.php.internal.debug.core.phpIni.PHPINIUtil; import org.eclipse.php.internal.debug.core.preferences.PHPProjectPreferences; import org.eclipse.php.internal.debug.core.preferences.PHPexeItem; import org.eclipse.php.internal.debug.core.preferences.PHPexes; import org.eclipse.php.internal.debug.core.zend.debugger.ProcessCrashDetector; import org.eclipse.php.internal.server.core.Server; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; /** * Mock debugger configuration that gives a possibility to create and use PHP * launch configurations without any debugger attached to corresponding debugger * owner (PHP server or executable). * * @author Bartlomiej Laczkowski */ @SuppressWarnings("restriction") public class NoneDebuggerConfiguration extends AbstractDebuggerConfiguration { public static final String ID = "org.eclipse.php.debug.core.noneDebugger"; //$NON-NLS-1$ private static final String NAME = "<none>"; //$NON-NLS-1$ public static final class ScriptLaunchDelegate extends LaunchConfigurationDelegate { /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.LaunchConfigurationDelegate# * preLaunchCheck (org.eclipse.debug.core.ILaunchConfiguration, * java.lang.String, org.eclipse.core.runtime.IProgressMonitor) */ @Override public boolean preLaunchCheck(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { PHPexeItem phpExeItem = PHPLaunchUtilities.getPHPExe(configuration); if (phpExeItem == null) { displayError(MessageFormat.format( PHPDebugCoreMessages.NoneDebuggerConfiguration_There_is_no_PHP_runtime_environment, configuration.getName())); return false; } String phpExeString = configuration.getAttribute(IPHPDebugConstants.ATTR_EXECUTABLE_LOCATION, (String) null); if (phpExeString == null || !(new File(phpExeString)).exists()) { displayError(MessageFormat.format( PHPDebugCoreMessages.NoneDebuggerConfiguration_PHP_executable_file_is_invalid, configuration.getName())); return false; } String fileName = configuration.getAttribute(IPHPDebugConstants.ATTR_FILE_FULL_PATH, (String) null); if (fileName == null || !(new File(fileName)).exists()) { displayError( MessageFormat.format(PHPDebugCoreMessages.NoneDebuggerConfiguration_PHP_script_file_is_invalid, configuration.getName())); return false; } if ((mode.equals(ILaunchManager.DEBUG_MODE) || mode.equals(ILaunchManager.PROFILE_MODE))) { displayError(MessageFormat.format( PHPDebugCoreMessages.NoneDebuggerConfiguration_There_is_no_debugger_attached_for_PHP_executable, configuration.getName(), phpExeItem.getName())); ILaunchConfigurationWorkingCopy wc = configuration.getWorkingCopy(); wc.setAttribute(IDebugUIConstants.ATTR_PRIVATE, false); wc.doSave(); return false; } return true; } /* * (non-Javadoc) * * @see * org.eclipse.debug.core.model.ILaunchConfigurationDelegate#launch( * org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, * org.eclipse.debug.core.ILaunch, * org.eclipse.core.runtime.IProgressMonitor) */ @Override public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { // Check for previous launches. if (!PHPLaunchUtilities.notifyPreviousLaunches(launch)) { monitor.setCanceled(true); monitor.done(); return; } String phpExeString = configuration.getAttribute(IPHPDebugConstants.ATTR_EXECUTABLE_LOCATION, (String) null); String phpIniPath = configuration.getAttribute(IPHPDebugConstants.ATTR_INI_LOCATION, (String) null); String fileName = configuration.getAttribute(IPHPDebugConstants.ATTR_FILE_FULL_PATH, (String) null); IProject project = null; String file = configuration.getAttribute(IPHPDebugConstants.ATTR_FILE, (String) null); if (file != null) { IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(file); if (resource != null) { project = resource.getProject(); } else { String projectName = configuration.getAttribute(IPHPDebugConstants.PHP_Project, (String) null); if (projectName != null) { IProject resolved = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); if (resolved != null && resolved.isAccessible()) { project = resolved; } } } } if (monitor.isCanceled()) { return; } IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 10); /* * Locate the php.ini by using the attribute. If the attribute was * null, try to locate an php.ini that exists next to the * executable. */ File phpIni = (phpIniPath != null && new File(phpIniPath).exists()) ? new File(phpIniPath) : PHPINIUtil.findPHPIni(phpExeString); File tempIni = PHPINIUtil.prepareBeforeLaunch(phpIni, phpExeString, project); launch.setAttribute(IDebugParametersKeys.PHP_INI_LOCATION, tempIni.getAbsolutePath()); // Resolve location IPath phpExe = new Path(phpExeString); String[] envp = DebugPlugin.getDefault().getLaunchManager().getEnvironment(configuration); File phpExeFile = new File(phpExeString); String phpIniLocation = launch.getAttribute(IDebugParametersKeys.PHP_INI_LOCATION); // Determine PHP configuration file location: String phpConfigDir = phpExeFile.getParent(); if (phpIniLocation != null && !phpIniLocation.equals("")) { //$NON-NLS-1$ phpConfigDir = new File(phpIniLocation).getParent(); } // Detect PHP SAPI type: String sapiType = null; String phpVersion = null; PHPexeItem[] items = PHPexes.getInstance().getAllItems(); for (PHPexeItem item : items) { if (item.getExecutable().equals(phpExeFile)) { sapiType = item.getSapiType(); phpVersion = item.getVersion(); break; } } String[] args = PHPLaunchUtilities.getProgramArguments(launch.getLaunchConfiguration()); String[] cmdLine = PHPLaunchUtilities.getCommandLine(launch.getLaunchConfiguration(), phpExeString, phpConfigDir, fileName, PHPexeItem.SAPI_CLI.equals(sapiType) ? args : null, phpVersion); // Set library search path: String libPath = PHPLaunchUtilities.getLibrarySearchPathEnv(phpExeFile.getParentFile()); if (libPath != null) { String[] envpNew = new String[envp == null ? 1 : envp.length + 1]; if (envp != null) { System.arraycopy(envp, 0, envpNew, 0, envp.length); } envpNew[envpNew.length - 1] = libPath; envp = envpNew; } if (monitor.isCanceled()) { return; } File workingDir = new File(fileName).getParentFile(); Process process = workingDir.exists() ? DebugPlugin.exec(cmdLine, workingDir, envp) : DebugPlugin.exec(cmdLine, null, envp); // Attach a crash detector new Thread(new ProcessCrashDetector(launch, process)).start(); IProcess runtimeProcess = null; // Add process type to process attributes Map<String, String> processAttributes = new HashMap<String, String>(); String programName = phpExe.lastSegment(); String extension = phpExe.getFileExtension(); if (extension != null) { programName = programName.substring(0, programName.length() - (extension.length() + 1)); } programName = programName.toLowerCase(); processAttributes.put(IProcess.ATTR_PROCESS_TYPE, programName); if (process != null) { subMonitor = new SubProgressMonitor(monitor, 90); subMonitor.beginTask(MessageFormat.format(PHPDebugCoreMessages.NoneDebuggerConfiguration_Launching, new Object[] { configuration.getName() }), IProgressMonitor.UNKNOWN); runtimeProcess = DebugPlugin.newProcess(launch, process, phpExe.toOSString(), processAttributes); if (runtimeProcess == null) { process.destroy(); throw new CoreException(new Status(IStatus.ERROR, PHPDebugPlugin.getID(), 0, null, null)); } subMonitor.done(); } if (runtimeProcess != null) { runtimeProcess.setAttribute(IProcess.ATTR_CMDLINE, fileName); } } } public static final class WebLaunchDelegate extends LaunchConfigurationDelegate { // Opens launch URL an gracefully dies. private final class MockProcess extends Process { private String launchURL; private MockProcess(String launchURL) { this.launchURL = launchURL; } private OutputStream outputStream = new OutputStream() { @Override public void write(int b) throws IOException { // ignore } }; private InputStream inputStream = new InputStream() { @Override public int read() throws IOException { return -1; } }; private InputStream errorStream = new InputStream() { @Override public int read() throws IOException { return -1; } }; @Override public int waitFor() throws InterruptedException { try { PHPDebugUtil.openLaunchURL(launchURL); } catch (DebugException e) { Logger.logException("Error while opening launch URL.", e); //$NON-NLS-1$ } return 0; } @Override public OutputStream getOutputStream() { return outputStream; } @Override public InputStream getInputStream() { return inputStream; } @Override public InputStream getErrorStream() { return errorStream; } @Override public int exitValue() { throw new IllegalThreadStateException(); } @Override public void destroy() { // ignore } } private static final String LAUNCH_LISTENERS_EXTENSION_ID = "org.eclipse.php.debug.core.phpLaunchDelegateListener"; //$NON-NLS-1$ private List<ILaunchDelegateListener> preLaunchListeners = new ArrayList<ILaunchDelegateListener>(); public WebLaunchDelegate() { registerLaunchListeners(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.LaunchConfigurationDelegate# * preLaunchCheck (org.eclipse.debug.core.ILaunchConfiguration, * java.lang.String, org.eclipse.core.runtime.IProgressMonitor) */ @Override public boolean preLaunchCheck(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { Server server = PHPLaunchUtilities.getPHPServer(configuration); if (server == null) { displayError(MessageFormat.format( PHPDebugCoreMessages.NoneDebuggerConfiguration_There_is_no_PHP_server_specified, configuration.getName())); return false; } String fileName = configuration.getAttribute(Server.FILE_NAME, (String) null); if (fileName == null) { displayError( MessageFormat.format(PHPDebugCoreMessages.NoneDebuggerConfiguration_PHP_source_file_is_invalid, configuration.getName())); return false; } if ((mode.equals(ILaunchManager.DEBUG_MODE) || mode.equals(ILaunchManager.PROFILE_MODE))) { Server phpServer = PHPLaunchUtilities.getPHPServer(configuration); displayError(MessageFormat.format( PHPDebugCoreMessages.NoneDebuggerConfiguration_There_is_no_debugger_attached_for_PHP_server, configuration.getName(), phpServer.getName())); ILaunchConfigurationWorkingCopy wc = configuration.getWorkingCopy(); wc.setAttribute(IDebugUIConstants.ATTR_PRIVATE, false); wc.doSave(); return false; } return true; } /* * (non-Javadoc) * * @see * org.eclipse.debug.core.model.ILaunchConfigurationDelegate#launch( * org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, * org.eclipse.debug.core.ILaunch, * org.eclipse.core.runtime.IProgressMonitor) */ @Override public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { // Notify all listeners of a pre-launch event. int resultCode = notifyPreLaunch(configuration, mode, launch, monitor); if (resultCode != 0) { // cancel launch monitor.setCanceled(true); monitor.done(); return; // canceled } // Check for previous launches if (!PHPLaunchUtilities.notifyPreviousLaunches(launch)) { monitor.setCanceled(true); monitor.done(); return; } String fileName = configuration.getAttribute(Server.FILE_NAME, (String) null); // Get the project from the file name IPath filePath = new Path(fileName); IProject project = null; try { project = ResourcesPlugin.getWorkspace().getRoot().getProject(filePath.segment(0)); } catch (Throwable t) { // ignore } if (project == null) { return; } ILaunchConfigurationWorkingCopy wc = configuration.getWorkingCopy(); String projectLocation = project.getFullPath().toString(); wc.setAttribute(IPHPDebugConstants.PHP_Project, projectLocation); // Set transfer encoding: wc.setAttribute(IDebugParametersKeys.TRANSFER_ENCODING, PHPProjectPreferences.getTransferEncoding(project)); wc.setAttribute(IDebugParametersKeys.OUTPUT_ENCODING, PHPProjectPreferences.getOutputEncoding(project)); wc.setAttribute(IDebugParametersKeys.PHP_DEBUG_TYPE, IDebugParametersKeys.PHP_WEB_PAGE_DEBUG); wc.doSave(); final String launchURL = new String(configuration.getAttribute(Server.BASE_URL, "") //$NON-NLS-1$ .getBytes()); launch.setAttribute(IDebugParametersKeys.WEB_SERVER_DEBUGGER, Boolean.toString(true)); launch.setAttribute(IDebugParametersKeys.ORIGINAL_URL, launchURL); DebugPlugin.newProcess(launch, new MockProcess(launchURL), launchURL, new HashMap<String, String>()); } protected int notifyPreLaunch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) { for (ILaunchDelegateListener listener : preLaunchListeners) { int returnCode = listener.preLaunch(configuration, mode, launch, monitor); if (returnCode != 0) { return returnCode; } } return 0; } /** * Registers all pre-launch listeners. */ private void registerLaunchListeners() { IConfigurationElement[] config = Platform.getExtensionRegistry() .getConfigurationElementsFor(LAUNCH_LISTENERS_EXTENSION_ID); try { for (IConfigurationElement e : config) { final Object o = e.createExecutableExtension("class"); //$NON-NLS-1$ if (o instanceof ILaunchDelegateListener) { ISafeRunnable runnable = new ISafeRunnable() { public void run() throws Exception { ILaunchDelegateListener listener = (ILaunchDelegateListener) o; Assert.isNotNull(listener); preLaunchListeners.add(listener); } public void handleException(Throwable exception) { Logger.logException(exception); } }; SafeRunner.run(runnable); } } } catch (CoreException ex) { Logger.logException(ex); } } } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger.IDebuggerConfiguration# * openConfigurationDialog(org.eclipse.swt.widgets.Shell) */ @Override public void openConfigurationDialog(Shell parentShell) { // Not supported } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger.IDebuggerConfiguration# * getAttribute(java.lang.String) */ @Override public String getAttribute(String id) { // Not supported return null; } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger.IDebuggerConfiguration# * getPort () */ @Override public int getPort() { // Not supported return -1; } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger.IDebuggerConfiguration# * getName () */ @Override public String getName() { return NAME; } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger.IDebuggerConfiguration# * getScriptLaunchDelegateClass() */ @Override public String getScriptLaunchDelegateClass() { return ScriptLaunchDelegate.class.getName(); } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger.IDebuggerConfiguration# * getWebLaunchDelegateClass() */ @Override public String getWebLaunchDelegateClass() { return WebLaunchDelegate.class.getName(); } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger.IDebuggerConfiguration# * getDebuggerId() */ @Override public String getDebuggerId() { return ID; } /* * (non-Javadoc) * * @see * org.eclipse.php.internal.debug.core.debugger.IDebuggerConfiguration#save * () */ @Override public void save() { // Not supported } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger. * AbstractDebuggerConfiguration#setPort(int) */ @Override public void setPort(int port) { // Not supported } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger. * AbstractDebuggerConfiguration#getModuleId() */ @Override public String getModuleId() { // Not supported return NAME; } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger. * AbstractDebuggerConfiguration#applyDefaults() */ @Override public void applyDefaults() { // Not supported } /* * (non-Javadoc) * * @see org.eclipse.php.internal.debug.core.debugger. * AbstractDebuggerConfiguration#validate(PHPexeItem) */ @Override public IStatus validate(PHPexeItem item) { // Not supported return Status.OK_STATUS; } private static void displayError(final String message) { final Display display = Display.getDefault(); display.asyncExec(new Runnable() { public void run() { MessageDialog.openError(display.getActiveShell(), PHPDebugCoreMessages.Debugger_LaunchError_title, message); } }); } }