9
Nov/09
0

A Java Swing Application for Testing PHP Snippets

I occasionally find myself needing to try out short chunks of PHP code. For example, to try out a regular expression, or to see how a built-in function behaves if it’s not documented very well. Creating a file, moving it to my web server’s document root, loading it up in the browser often seems too much trouble. So I’ve put together a simple application using Swing to make this easier.

Overview of the Application

Let me summarise the functionality of the application, incase you just want to try it out. The app allows you to enter PHP code and then execute it without having to save it to file. You see the results instantly in the bottom half of the window.

There is also a built-in, simple (single-threaded) web server. This means you can view the results of execution in your browser—again, without having to save the file.

php-snippet-tester

I’m going to explain some of the technical aspects of putting the app together. If you’re just interested in using the app, scroll down to the bottom of the post for the download link.

Working with the Command Line from Java

PHP can be run from the command line. Details about this are on the PHP website, but in short, if you run php with no arguments, then it will take in source code from the standard input, execute it, and print out the result.

This can be achieve programatically from within the Java environment:

// Get the runtime object
Runtime rt = Runtime.getRuntime();
 
// Run the command
Process process = rt.exec(PHP_COMMAND);
 
// Setup buffered input and output streams
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedWriter output = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
 
// Send the source code and close the stream (to make EOF)
output.write(code);
output.close();
 
// Read in the result, line by line and print it to the console
String line;
while((line=input.readLine()) != null) {
    System.out.println(line);
}
 
// Wait for the process to terminate (not really necessary, because the process will terminate once it has closed the input stream anyway)
int result = process.waitFor();

Note that rt.exec(...) and process.waitFor() can both potentially throw exceptions—IOException and InterruptedException respectively—which must be dealt with.

We could get the standard error stream aswell—using the same approach as with the output stream—but PHP doesn’t send anything to the error stream, so we don’t need to worry about it. If we wanted to do this, we’d need to read both streams in separate threads.

Building the Swing User Interface

Let’s look at how to setup the user interface. I’ll talk through a simplified version of the final application. We’ll look at a few of the Swing components individually.

JFrame

We’re going to make use of the JFrame class as our base. A common practice is to extend this class with your own, however, generally a more practical approach is to encapsulate the JFrame object by keeping track of the JFrame as a property within your class, and exposing methods as necessary.

We create a JFrame, set a layout (refer to the next section), a preferred size, the behaviour to associate with the window’s ‘close’ button, pack it (to make sure the components get arranged properly) and then finally make the frame visible to the user:

frame = new JFrame(FRAME_TITLE);
 
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(500, 500));
 
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
// Add components to the frame here
 
frame.pack();
frame.setVisible(true);

BorderLayout

Once the JFrame has been setup, we’ll use a BorderLayout to arrange our components on the window. Border layouts are a fairly common concept in user interface design. One component can be attached to each of five regions. Each region defines the position that the component appears at, and how its size changes as the parent component is resized. Border layouts can be nested within each other. The regions are:

  • PAGE_START – generally at the top of the page, resizes to fill the width of the parent component.
  • PAGE_END – opposite side to PAGE_START.
  • LINE_START – generally on the left of the page (but actually dependent on the user’s language), resizes to fill the height of the parent minus the height of any components at PAGE_START and PAGE_END.
  • LINE_END – opposite side to LINE_END.
  • CENTER – the remaining space in the middle of the layout, resizes to fill the available space.

See the example below, which shows the positioning of the components:

border-layout

JSplitPane

For our user interface we will divide the window into two: the top half for editing the PHP code, and the bottom half for viewing the results of the execution. We will allow the user to move the separator between the two components using a JSplitPane.

Using a JSplitPane is fairly straight forward. Pass each of the two components you’re splitting into the constructor, along with a specified orientation (either VERTICAL_SPLIT or HORIZONTAL_SPLIT). The location of the divider can also be given.

JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, inputPanel, outputPanel);
splitPane.setDividerLocation(200);

JTextArea and JScrollPane

Both the editor and the area for displaying execution results will be based on JTextAreas. On their own, these components don’t support scrolling (e.g., for when there are more lines of text than will fit in the component). To support scrolling, we need to wrap them with JScrollPanes.

JScrollPane inputScrollPane = new JScrollPane(inputBox);

JButton and Actions

When the user wants to execute their code, they will click on an ‘Execute’ button (which will be a JButton). There are a few ways to handle button clicks in Swing. One such method is to define an Action that will be associated with the button. This is a nice way to separate logic from the design of the user interface.

A button can be created like this:

JButton executeButton = new JButton(new ExecuteAction(inputBox, outputBox));

The easiest way to create the action is to extend from AbstractAction:

public class ExecuteAction extends AbstractAction {
 
    private JTextArea inputBox;
    private JTextArea outputBox;
 
    public ExecuteAction(JTextArea inputBox, JTextArea outputBox) {
 
        // Save these components for later
        this.inputBox = inputBox;
        this.outputBox = outputBox;
 
        // Set the name of the button
        putValue(AbstractAction.NAME, "Execute");
    }
 
    public void actionPerformed(ActionEvent e) {
        // Event will be handled here
    }
}

Any objects that the action needs to access get passed to the constructor, and stored as properties of the action for use later. In this case, we’ll want to be able to get the source code from the input box and then put the result into the output box.

Putting Together the PhpRunner Class

We will put our earlier PHP-running code into it’s own class and package (runner.PhpRunner). The class will be constructed with two parameters:

  • The PHP code to be run
  • An event listener (an object implementing the OutputConsumer interface)

The execute() method can then be called. This will start the execution, and notify the event listener every time we get a new line of output back. This approach means that if a PHP script takes a long time to complete, we’ll be able to see the output as it happens, rather than having to wait for the whole script to finish executing.

The code is similar to the code at the top of the post, so I won’t paste it again. I have added calls to the event listener though. Refer to the source code if you’re interested.

Threads and Thread Safety

Introducing threads into a Swing application can be dangerous, since Swing’s components are not thread safe. Only one thread should be allowed to access the Swing components at any one time—this is known as the event-dispatching thread (EDT).

However, we don’t want the user interface to freeze whilst the PHP code is being executed, so it’s important that we don’t wait for output from within the EDT. The swing library provides a SwingWorker class, which makes working with threads a bit easier and safer. Fortunately, our PhpRunner fits nicely into this pattern. A cut-down version of the code looks like this:

new SwingWorker<String, String>() {
 
    @Override
    protected String doInBackground() throws Exception {
        // Setup the PHP runner, and listen for any lines of output
        PhpRunner runner = new PhpRunner(code, new OutputConsumer() {
            @Override
            public void output(String line) {
                // Let the SwingWorker know there are more lines
                publish(line);
            }
        });
        // Execute, and return the result
        return runner.execute();
    }
 
    @Override
    protected void process(List<String> chunks) {
        // Add any output to the output box
        for (String chunk : chunks) {
            outputBox.append(chunk + "\n");
        }
    }
 
    @Override
    protected void done() {
        // The operation has completed
    }
}.execute();

Adding a Built-in Web Server

Sometimes it’s useful to be able to test the PHP output in a web browser. So we will add a very basic web server to the application. The user will be able to request execution of the code by loading a URL in their web browser.

First of all, we need to add a button to the user interface to allow the user to manually start and stop the web server. When the associated action is performed, we will either create a new HTTP server object, or stop an existing one.

The HTTP server doesn’t need to be very complicated at all. We don’t have to handle POST requests (just GET and HEAD requests) or even URLs since all requests to the server will execute the same script. We also only need to handle one request at a time—so the server can happily run on a single thread.

I don’t want to go into details of the server implementation here, but it’s worth nothing that this use of a web server does bring up the slightly unconventional approach of trying to get the contents of a Swing component from outside the EDT:

final String[] value = {""};
try {
    SwingUtilities.invokeAndWait(new Runnable() {
        public void run() {
            value[0] = inputBox.getText();
        }
    });
} catch (Exception e) {
    // Do nothing
}
return value[0];

The value variable must be declared final, since it’s accessed from a different thread. However, we want to be able to update the value, so the use of a single-element array is a bit of a hack to bypass the compiler’s warnings. A more elegant solution to this might involve wrapping the method in it’s own class, and using a class property instead of a final variable.

Saving User Settings with the Preferences API

Java provides an API that makes it very straight-forward to store and retrieve user preferences. These are used in our application to save a number of different parameters:

  • The position and location of the window on the screen.
  • The position of the resizable split pane.
  • The last-used PHP code.
  • The specified font size.
  • The directory of the last file that was loaded.

We can wrap the Preferences class in our own Settings class, which provides methods to get the required properties. Attaching a WindowListener to our JFrame means that we can attempt to save the settings when the application is closed.

Conclusions

I ended up making a few more changes to the application to those mentioned above. The buttons that are referred to above have been move to the menu bar (which was easy to do, since I was using Actions), and have associated keyboard shortcuts which make the application much easier to use. It’s now also possible to load files from disk, change the font size of the input and output boxes. I’ve also added a status bar which shows status messages for a period of time, and a ‘preferences’ dialog which allows you to change the PHP command path and the web server port.

If the implementation of any of these features interests you, I suggest you download the source code to have a look.

To get a grasp of the structure of the project, the layout below may help:

project-layout

I’m very pleased with the final product. The application’s concept is simple, but I think it adheres well to the “do one thing and do it well” philosophy.

I’ve also made a few minor tweaks to the application that will affect Mac users: moving the menu bar to the top of the screen, and setting the application’s title.

Download

If you’d like to download the application to give it a try yourself, or if you’d like to download the source code in its entirety, use the links below:

If you do try out the application, please let me know what you think.

Requirements

The application requires that you have both Java (version 1.6—although 1.5 might work) and PHP installed. The PHP executable must be accessible on your ‘path’. I have tested this on OS X and on a vanilla install of Vista (after installing Java and PHP).

Filed under: Java, PHP, Swing
Comments (0) Trackbacks (0)

No comments yet.

Leave a comment

No trackbacks yet.