Friday, August 14, 2009

Browsing the Web in PHP

Recently, I was working with some PHP code that contacts AuthorizeNet to handle credit card payments. This process involves submitting POST data to an AuthorizeNet server over a secure connection, and decoding the results. Since it was written by a skilled coworker, it transmitted the POST data in an interesting way: using stream contexts.

What is POST Data?

Have you ever filled out a form on a Web page? When you click the Submit button, your browser packages your entries into POST data. It then sends them in a POST request to the Web site's server. Now what if you want to write a PHP script that pretends to be you, submitting the form data to that server using PHP code? That's basically how communication with AuthorizeNet works.

Stream Contexts: Turning fopen() into a "browser"

Most people who have worked with PHP or C know fopen() can open files. However, in PHP, it can also open URLs. You can use fopen() to download the HTML of the Web page at the URL you pass it. Here's the code for submitting some data over SSL (from the PHP manual page):

$data = array ('foo' => 'bar', 'bar' => 'baz');
$data = http_build_query($data);

$context_options = array (
'http' => array (
'method' => 'POST',
'header'=> "Content-type: application/x-www-form-urlencoded\r\n"
. "Content-Length: " . strlen($data) . "\r\n",
'content' => $data
)
);

$context = context_create_stream($context_options)
$fp = fopen('https://url', 'r', false, $context);
?>

  1. The POST data you want to send is transformed into a HTTP query string. Basically, that array becomes the string "foo=bar&bar=baz".
  2. Then, you create a context_options array that states the options for the HTTP request.
  3. The fopen() call opens the URL 'https://url' as if it were a file. It returns a stream resource, which is stored in the variable $fp. You can pass $fp to PHP's other stream functions to read the contents of the page.
You can get the page as a string by calling stream_get_contents($fp), or just output it directly with fpassthru($fp). Pretty neat, huh?

And Now for the Bugs...

There are a lot of subtleties to coding PHP. On Windows, I noticed the http_build_query function seems to use & amp; instead of & for the delimiter. Apparently, the delimiter is determined by the value of "arg_separator.output" in php.ini. You can fix this by calling http_build_query($data, '', '&').

How Is This Useful?

It's a remarkably simple way to send POST data to a server. This means you could, in theory, write your own bots for adding comments to people's pages (assuming they don't have captchas). I'm also thinking about using it to automatically submit my University of Washington schedule at 6 AM when my registration period begins. That way, I can ensure I'll get a seat in the classes I need, because my server will have signed me up before anyone else gets a chance. I also won't have to wake up at 6 AM. I could also write a proxy server with it. Maybe I'll do all this in the future, but for now -- it lets me submit credit card payments!

No comments: