Basic GWT / PHP Communication (Part 2: PHP)

Now to the PHP side of the connection. There seems to be some confusion about what pages run where and under what circumstances during the development process. I’ve seen lots of talk about setting up proxies and such to deal with such issues. Hosted mode and web mode do have notable differences in operation.

In developing a Web Toolkit application, it is very helpful to debug it in hosted mode under Eclipse (as is often the case). To accomplish this, the application gets launched as a hosted application under Tomcat on the development machine. IE is the browser used to display this hosted application. This does present a minor challenge if you are talking to a remote server: your script will refer to a server different from where it was served. This is a case of cross-site scripting, which can be rather desirable. The good news (for developers) is that IE gives you the option to let it happen. So no problems there.

There have been many questions about how to run PHP scripts under Tomcat or trying to get the page to access something other than port 8888, or other similar questions. To deal with this in development, I use an absolute URL when calling HTTPRequest.asyncPost. So the whole thing:
“http://www.drivenbycuriosity.com/sample_files/ServerComm.php”
as used in ServerComm.java from the first part of this guide. For this to work, the script does need to be loaded on a server — even if it’s apache running on your development machine. Just remember to keep your document root straight so that you can actually write a URL that will access your PHP script, wherever you keep it. I won’t completely restate my post from this blog, but I offer more advice along these lines here.

The PHP side is almost simpler to describe than the Java side, since the server environment is more implementation dependent. The tie-in to the ServerComm Java class is straightforward.

To address the issue of asyncPost not currently allowing the MIME type to be correctly set, it needs the following line of code:
parse_str(file_get_contents("php://input"), $_POST);

Due to the MIME type issue, PHP does not automatically load the data sent to it from GWT. But it is still available through the php://input stream. parse_str parses the data and stores it to a variable, in this case $_POST. This is nearly identical to the automatic handling with a proper MIME type. urldecode still seems to work even if the data includes characters that are not strictly valid (such as square brackets). This is a very simple workaround. And if you DO post valid data, such as with an HTML test page, it works without complaint.

There are up to three arrays passed to PHP from the Java class: refresh, sender, and data. The script loops through sender/data to obtain data passed back from ServerComm, then loops through refresh. Due to how the ServerComm class is written, refresh will also contain entries for objects referenced in sender.

The format for sending data back to the GWT client is as follows:
title1,val1:&:val2:&:val3\n
title2,data1

It is important to remove extra ‘\n’ characters from data being sent back. This could be an issue for textbox inputs and such, but it should be easy enough to add some functionality to encode/decode such needs.

My sample script also contains a utility function for debugging purposes: safe_printr. It uses ob_start / ob_get_contents and ob_end_clean to buffer the output of print_r and return it in a more pleasant form for display. Also, to assist in getting the server-side script running properly, I use an HTML file to send representative data to the script. This is helpful in clearing syntax errors and other situations where the script output does not conform to what the client expects. It is by all means possible to debug the output by setting break points and browsing variables while running in hosted mode, but I prefer to catch the errors when they display on a refreshable browser window rather than in Eclipse.

You can find the PHP skeleton here. This script will send back the time and a second string for any element title found in refresh[]. I also have a sample HTML debug file here. The text box and hidden input mimic the behavior of sending data back to PHP, the check boxes show the behavior of items in refresh[].

In an effort to provide an informative demo, the HTML file posts to a working version of the PHP script. This file is referenced in the url given in the Java class, so it should be easy to get started.

You can do the following:
– use applicationCreator to create a new project (or start with an existing project)
– add the package line to serverComm.java
– I changed the default new project java class as follows:

public class testWidget implements EntryPoint, ClickListener, ServerComm.ServerData {
final Label label = new Label();

/**
* This is the entry point method.
*/
public void onModuleLoad() {
final Button button = new Button(”Click me”);

button.addClickListener(this);

// Assume that the host HTML has elements defined whose
// IDs are “slot1″, “slot2″. In a real app, you probably would not want
// to hard-code IDs. Instead, you could, for example, search for all
// elements with a particular CSS class and replace them with widgets.
//
RootPanel.get(”slot1″).add(button);
RootPanel.get(”slot2″).add(label);
}

public void onClick(Widget sender) {
ServerComm.requestUpdate(this);
}
public String getTitle() { return “label”; }
public void serverDataHandler(String[] s) { label.setText(s[0]); }
}

After that, run it in hosted mode. When the button is clicked, the label text should be replaced by a time.