Porting a Java Program to PHP
In Processing XML with Java, I used an example of plain text, XML-RPC, and SOAP clients that communicate with a Fibonacci number server I run here on www.elharo.com. Originally I wrote the servers in Java using the Java Servlet API. However, problems with Tomcat meant that the server went down every few months. When I moved this server from Linux to a Mac I didn’t even bother to reinstall Tomcat, and instead decided to port the servers to PHP 5. PHP has its own idiosyncrasies, but I use it for other things on this server (including this blog), and it didn’t seem worth keeping Tomcat running just for a few simple scripts.
Porting the plain text Fibonacci server was straightforward since it’s based on GET and query strings. It’s just a few lines of code:
<?php
$generations = $_GET['generations'];
if ( $generations == 0) {
$generations = 10;
}
$low = 1;
$high = 1;
for ( $counter = 1; $counter <= $generations; $counter++) {
echo $low;
$oldhigh = $high;
$high = $high + $low;
$low = $oldhigh;
echo " ";
}
?>
The next two proved harder. PHP has always been aimed at hacking together web sites for browser delivery. It’s never worked well as soon as you attempt anything more complex than that. In particular, it expects to see POST requests as x-www-formurlencoded strings, just like GET requests. Start sending XML, whether XML-RPC or SOAP or XForms or something else, and it more-or-less punts.
When I first started doing this back in the days of PHP 3 it was simply impossible to respond to XML-RPC requests in PHP. (That’s a big reason I went with servlets in the first place.) Nowadays I understood that in PHP 4 and 5 it was possible to see the raw request body, and parse it as you liked using $HTTP_RAW_POST_DATA
(or $_SERVER[HTTP_RAW_POST_DATA]
or $GLOBALS[HTTP_RAW_POST_DATA]
; various references couldn’t seem to agree on this point) Anyway, that didn’t matter because none of those worked at all.
I inspected the source code for various PHP, SOAP, and XForms libraries and all of them seemed to be using $HTTP_RAW_POST_DATA
. One XForms library did suggest this:
if (!isset($HTTP_RAW_POST_DATA)) {
echo "Not set";
$HTTP_RAW_POST_DATA = file_get_contents("php://input");
}
That confirmed my suspicion that the variable wasn’t set; but still failed to give me the input data I needed. Fortunately I’m not the only person having trouble with this. Seems like there’s something called always_populate_raw_post_data
I may need to set in php.ini? So let’s add
always_populate_raw_post_data = On
to my php.ini file, backup the server, and then restart Apache. (I’m always paranoid before a server restart.) And still nothing happens! Bleah. Time to do a little more digging. Nothing on the web seems to help so I look at my own code. In particular I make the PHP script print out the client headers to see what it’s sending:
$ java FibonacciXMLRPCClient 10 GET Not set Debugging Array ( [UNIQUE_ID] => 3jinfcCo-igAAD1VqbUAAAAM [HTTP_USER_AGENT] => Java/1.5.0_06 ...
What the hell? It’s using GET?! Why is it doing that? Time to look at the client. Hmm, that looks OK:
URL u = new URL(server);
URLConnection uc = u.openConnection();
HttpURLConnection connection = (HttpURLConnection) uc;
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
OutputStream out = connection.getOutputStream();
OutputStreamWriter wout = new OutputStreamWriter(out, "UTF-8");
I do note I should probably be setting the Content-type header, but it is specifying POST. Why am I seeing GET coming into the server? Maybe I’m running an older version of the code? I try recompiling but that doesn’t fix it. I try printing out the URLConnection’s own opinion of its method.
URL u = new URL(server);
URLConnection uc = u.openConnection();
HttpURLConnection connection = (HttpURLConnection) uc;
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
System.err.println(connection.getRequestMethod());
OutputStream out = connection.getOutputStream();
OutputStreamWriter wout = new OutputStreamWriter(out, "UTF-8");
However, the client thinks it’s sending POST and the SERVER thinks it’s hearing GET:
$ java FibonacciXMLRPCClient 10 POST GET Not set ...
Someone’s lying to me and I don’t know who. Let’s try pointing the client at a different page, one that just uses phpinfo()
, and see what it thinks. OK, that page also thins the client is sending POST, not GET so something must be wrong with my script.
What could possibly be the difference between these two cases? What could be getting in my way and changing the POST to a GET? Here’s an idea: the phpinfo() URL was referenced directly, but the XML-RPC URL was referenced by pointing to its directory. I.e. I was loading http://www.elharo.com/fibonacci/XML-RPC instead of http://www.elharo.com/fibonacci/XML-RPC/index.php. Could that have something to do with it? Let’s try:
$ java FibonacciXMLRPCClient 10 http://www.elharo.com/fibonacci/XML-RPC/index.php POST POST ...
Bingo! That’s how the POST is changed to a GET. Further testing reveals that http://www.elharo.com/fibonacci/XML-RPC/ with a trailing slash retains the POST method but http://www.elharo.com/fibonacci/XML-RPC without a trailing slash switches it to GET.
I wonder if this is documented behavior or a bug in Apache 2 or PHP 5? And is there an obvious workaround?
If anyone has any ideas about how to fix this, please comment. In the meantime, I’m going to move on to the next step: implementing the XML-RPC response.
November 13th, 2006 at 11:21 AM
Since you didn’t specify the directory with a trailing slash, the server is sending a 301 Moved Permanently to the client, handing back a URL that ends in a slash. A URL like http://www.elharo.com/fibonacci/XML-RPC doesn’t actually point to a valid location and most servers return the 301 instead of erroring out, the only other option they’re allowed by the spec.
http://www.elharo.com/fibonacci/XML-RPC/ would probably have worked fine, too.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
Note: When automatically redirecting a POST request after
receiving a 301 status code, some existing HTTP/1.0 user agents
will erroneously change it into a GET request.
November 14th, 2006 at 5:54 AM
Aha! It is a client bug then. In this case the client is
I’ll have to file a bug report on this.
January 28th, 2007 at 6:43 PM
Thanks! I’ve been running into the same problem, and it’s been driving me nuts!