[ Team LiB ] Previous Section Next Section

Making a Network Connection

So far, we have had it easy. This is because PHP makes working with a Web page on a remote server as simple as opening a file on your own system. Sometimes, though, you need to exercise a little more control over a network connection or acquire more information about it.

You can make a connection to an Internet server with fsockopen(), which requires a hostname or an IP address, a port number, and two empty variables. The empty variables you pass to fsockopen() are populated to provide more information about the connection attempt should it fail. You can also pass fsockopen() an optional timeout integer, which determines how long fsockopen() will wait (in seconds) before giving up on a connection. If the connection is successful, a resource variable is returned; otherwise, it returns false.

The following fragment initiates a connection to a Web server:

$fp = fsockopen( "www.corrosive.co.uk", 80, $errno, errdesc, 30 );

80 is the usual port number a Web server listens on.

The first empty variable, $errno, contains an error number if the connection is unsuccessful, and $errdesc might contain more information about the failure.

After you have the file pointer, you can both write to the connection with fputs() and read from it with fgets() as you might with a file. When you have finished working with your connection, you should close it with fclose().

We now have enough information to initiate our own connection to a Web server. Listing 14.6 makes an HTTP connection, retrieving a page and storing it in a variable.

Listing 14.6 Retrieving a Web Page Using fsockopen()
 2:   "-//W3C//DTD XHTML 1.0 Strict//EN"
 3:   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 4: <html>
 5: <head>
 6: <title>Listing 14.6 Retrieving a Web page using fsockopen()</title>
 7: </head>
 8: <body>
 9: <div>
10: <?php
11: $host = "www.corrosive.co.uk";
12: $page = "/index.html";
13: $fp = fsockopen( "$host", 80, $errno, $errdesc );
14: if ( ! $fp ) {
15:   die ( "Couldn't connect to $host:\nError: $errno\nDesc: $errdesc\n" );
16: }
18: $request = "GET $page HTTP/1.0\r\n";
19: $request .= "Host: $host\r\n";
20: $request .= "Referer: http://www.corrosive.co.uk/refpage.html\r\n";
21: $request .= "User-Agent: PHP test client\r\n\r\n";
23: $page = array();
24: fputs ( $fp, $request );
25: while ( ! feof( $fp ) ) {
26:   $page[] = fgets( $fp, 1024 );
27: }
28: fclose( $fp );
29: print "the server returned ".(count($page))." lines!";
30: ?>
31: </div>
32: </body>
33: </html>

Notice the request headers (lines 18–21) we send to the server in line 24. The Webmaster at the remote host sees the value you sent in the User-Agent header in her log file. She also might assume that a visitor to our page connected from a link at http://www.corrosive.co.uk/refpage.html. For this reason, you should be cautious of some of the environment variables available to your scripts. Treat them as a valuable guide, rather than a set of facts.

There are some legitimate reasons you might want to fake some headers. You might need to parse some data that will be sent only to Netscape-compatible browsers. One way you can do this is to include the word "Mozilla" in the User-Agent header. Nevertheless, pity the poor Webmaster. Operational decisions are made as a result of server statistics, so try not to distort the information you provide.

The example in Listing 14.6 adds little to PHP's built-in method of acquiring Web pages. Listing 14.7 uses fsockopen() to check the status codes returned by servers when we request a series of pages.

Listing 14.7 Outputting the Status Lines Returned by Web Servers
 2:   "-//W3C//DTD XHTML 1.0 Strict//EN"
 3:   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 4: <html>
 5: <head>
 6: <title>Listing 14.7 Outputting Server Status Lines</title>
 7: </head>
 8: <body>
 9: <div>
10: <?php
11: $to_check = array (
12:           "www.corrosive.co.uk" => "/index.html",
13:           "www.virgin.com"    => "/notthere.html",
14:           "www.4332blah.com"   => "/nohost.html"
15:       );
17: foreach ( $to_check as $host => $page ) {
18:   print "<p>\n";
19:   $fp = @fsockopen( "$host", 80, $errno, $errdesc, 10);
20:   print "Trying $host<br/>\n";
21:   if ( ! $fp ) {
22:     print "Couldn't connect to $host:<br/>\n";
23:     print "Error: $errno<br/>\n";
24:     print "Desc: $errdesc<br/>\n";
25:   } else {
26:     print "Trying to get $page<br/>\n";
27:     fputs( $fp, "HEAD $page HTTP/1.0\r\n" );
28:     fputs( $fp, "Host: $host\r\n" );
29:     fputs( $fp, "\r\n" );
30:     print fgets( $fp, 1024 );
31:     fclose( $fp );
32:   }
33:   print "</p>\n";
34: }
36: ?>
37: </div>
38: </body>
39: </html>

We create an associative array of the server names and page addresses we want to check starting at line 11. We loop through this using a foreach statement on line 17. For every element, we initiate a connection using fsockopen() (line 19), setting a timeout of 10 seconds. If the connection fails, we print a message to the browser. If the connection is successful, however, we send a request to the server on lines 27–29. We use the HEAD method because we are not interested in parsing an entity body. Notice that we send a Host header, which is required to ensure that the correct site is referenced for a server with multiple virtual hosts. We use fgets() on line 30 to get the status line from the server. We are not going to work with server headers for this example, so we close the connection with fclose() on line 31 and move onto the next element in the list.

Figure 14.2 shows the output from Listing 14.7.

Figure 14.2. A script to print server response headers.



If you are interested in writing sophisticated Web client applications, you should look at the CURL package (http://curl.haxx.se/). As of PHP 4.02, support was added for CURL which can handle many of HTTP's more tricky aspects, including user and password authentication, cookies, and POST form submissions. It can also handle secure transactions with HTTPS and a range of other protocols. You can get more details from the PHP manual at http://www.php.net/manual/en/ref.curl.php.

Making an NNTP Connection Using fsockopen()

fsockopen() can be used to make a connection to any Internet server. In Listing 14.8, we connect to an NNTP (Usenet) server, select a newsgroup, and list the headers of the first message.

Listing 14.8 A Basic NNTP Connection Using fsockopen()
 2:   "-//W3C//DTD XHTML 1.0 Strict//EN"
 3:   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 4: <html>
 5: <head>
 6: <title>Listing 14.8 A basic NNTP Connection Using fsockopen()</title>
 7: </head>
 8: <body>
 9: <?php
10: $server = "news"; // change this to your news server
11: $group = "sci.physics";
12: $line = " ";
13: print "<pre>\n";
14: print "-- Trying to connect to $server\n\n";
16: $fp = @fsockopen( "$server", 119, $error, $description, 10 );
17: if ( ! $fp ) {
18: die("Couldn't connect to $server\n$errno\n$errdesc\n\n");
19: }
21: print "-- Connected to $server\n\n";
23: $line = fgets( $fp, 1024 );
24: $status = explode( " ", $line );
26: if ( $status[0] != 200 && $status[0] != 201 ) {
27:   fputs( $fp, "close" );
28:   die("Error: $line\n\n");
29: }
31: print "$line\n";
32: print "-- Selecting $group\n\n";
33: fputs( $fp, "group $group\n" );
34: $line = fgets( $fp, 1024 );
35: $status = explode( " ", $line );
37: if ( $status[0] != 211 ) {
38:   fputs( $fp, "close" );
39:   die("Error: $line\n\n");
40: }
42: print "$line\n";
43: print "-- Getting headers for first message\n\n";
44: fputs( $fp, "head\n" );
45: $line = fgets( $fp, 1024 );
46: $status = explode( " ", $line );
47: print htmlspecialchars("$line\n");
49: if ( $status[0] != 221 ) {
50:   fputs( $fp, "close" );
51:   die("Error: $line\n\n");
52: }
54: while ( ! ( strpos($line, ".") === 0 ) ) {
55:   $line = fgets( $fp, 1024 );
56:   print htmlspecialchars($line);
57: }
59: fputs( $fp, "close\n" );
60: print "</pre>";
61: ?>
62: </body>
63: </html>

The code in Listing 14.8 does little more than demonstrate that an NNTP connection is possible with fsockopen(). In a real-world example, you would want to handle the line parsing in a function to save repetition and extract more information from the server's output. Rather than reinvent the wheel in this way, you might want to investigate PHP's IMAP functions, which provide POP3 and NNTP connectivity and automate much of this work for you. On the other hand, the example does illustrate the power and potential of PHP as a network-capable language.

We store the hostname of our server in a variable—$server—on line 10 and store the group we want to select in $group on line 11. If you want to run this script, you should assign the hostname of your ISP's news server to the $server variable.


If your ISP does not allow you access to a news server, you might be able to run this script on a public news server. An excellent resource for public servers can be found at http://www.newzbot.com/.

We use fsockopen() on line 16 to connect to the host on port 119, which is the usual port for NNTP connections. If a valid file resource is not returned, we use die() on line 18 to print the error number and description to the browser and end script execution. On connection, the server should have sent us a confirmation message, so we attempt to acquire this with fgets() on line 23. If all is well, this string begins with the status code 200. To test this, we use explode() (on line 24) to split the $line string into an array using the space character as the delimiter. To learn more about the explode() function, refer to Hour 8. If the first element of this array is 200 or 201 (the status returned by servers that do not allow posting), we can continue; otherwise, we end the script.

If all is proceeding as expected, we send the news server the "group" command that should select a newsgroup on line 33. If this is successful, the server should return a string beginning with the status code 211. We test this again on line 37 and end execution if we don't get what we are expecting.

Now that we have selected our newsgroup, we send the "head" command to the server on line 44, which requests the headers for the first message in the group. Again, we test the server response on line 49, looking for the status code 221. Finally, we acquire the header itself. The server's listing of a header ends with a single dot (.) on its own line, so we test for this in a while statement on line 49. As long as the server's output line does not begin with a dot, we request and print the next line.

Finally, we close the connection. Figure 14.3 shows a typical output from Listing 14.8.

Figure 14.3. Making an NNTP connection.


    [ Team LiB ] Previous Section Next Section