1
45

sendhttp

sendhttp
SYNOPSIS

sendhttp($method, $url, $args, $files=false, $base64=false)

DESCRIPTION

sendhttp produces a GET or a POST HTTP 1.0 document, transmits it to an HTTP or HTTPS server and returns the HTTP code, the header and the body of the document sent back by the server or false in case of error.

$method is set to 'GET' or 'POST' depending on the type of request expected by the server.

$url addresses the PHP code of the server which is going to analyze the document, run an action and return a regular HTTP document. $url has the format [proto://]host[:portnum]/path. Set proto to 'https' to transmit a document in a secure mode. host gives the name or the IP address of the server. If necessary, add portnum to specify a port number different from 80 or 443 for HTTP or HTTPS connections with a server listening to particular ports. path gives the path to the PHP code on the server.

$args contains a list of parameters for the called service arranged in an associative array { 'param1' => val1, ... }. $args can pass one or several fields directly in a URL for an HTTP GET request or extracted from an HTML form for an HTTP POST request.

$files contains the list of files attached to the request in the form an associative array { 'docname' => {'name' => 'filename', 'type' => 'mimetype', 'tmp_name' => 'pathname'}, ... }. This parameter is optional. Typically, it's used to pass a file transmitted by an <input type="file" name="docname"... /> tag in an HTML form. If $base64 is true, the contents of the files are encoded in base64.

sendhttp sends back an array containing the HTTP return code, the header of the response and the body of the response. If an argument is invalid or if the connection has failed, sendhttp returns false.

EXAMPLE
php> $r = sendhttp('GET', 'http://www.google.com/search', array('q' => 'izend'));

Same as typing http:://www.google.com/search?q=izend in the address bar of a navigator.

php> print_r($r[0]);
200

Displays the HTTP return code.

php> print_r($r[1]);
Array
(
    [Date] => ...
    [Expires] => -1
    [Cache-Control] => private, max-age=0
    [Content-Type] => text/html; charset=ISO-8859-1
    ...
)

Displays the MIME header of the document.

php> echo $r[1]['Content-Type'];
text/html; charset=ISO-8859-1

Displays the content type of the document.

php> print_r($r[2]);
<!doctype html><head><title>izend - Google Search</title> ...

Displays the document.

CODE
  1. function sendhttp($method, $url, $args, $files=false, $base64=false, $options=false, $header=false) {

sendhttp accepts 4 arguments: a request type, a URL, a liste of parameters and an optional list of files.

  1.     $r = http_parse_url($url);
  2.  
  3.     if (!$r) {
  4.         return false;
  5.     }
  6.  
  7.     list($proto, $scheme, $host, $portnum, $path)=$r;

Decomposes the URL with the PHP function parse_url. Returns false if $url is invalid.

  1.     $hostaddr=($scheme == 'http' && $portnum == 80) ? $host : $host . ':' . $portnum;
  2.  
  3.     $user_agent='iZend';
  4.  
  5.     $header_string=$content_string='';
  6.  
  7.     $crlf="\r\n";
  8.  
  9.     switch ($method) {

Initializes the values of the fields Host: and User-Agent: of the request. Prepares the header and the body of the HTTP document. Defines $crlf to the value of the end of line of MIME document. Tests the type of the request.

  1.         case 'POST':
  2.             if ($files && is_array($files)) {
  3.                 $boundary = md5(microtime());
  4.                 $content_type = 'multipart/form-data; boundary='.$boundary;
  5.  
  6.                 $content_string = '';
  7.  
  8.                 if ($args && is_array($args)) {
  9.                     foreach ($args as $k => $v) {
  10.                         $content_string .= '--' . $boundary . $crlf;
  11.                         $content_string .= 'Content-Disposition: form-data; name="' . $k . '"' . $crlf . $crlf . $v . $crlf;
  12.                     }
  13.                 }
  14.                 foreach ($files as $k => $v ) {
  15.                     if (isset($v['tmp_name'])) {
  16.                         $data = file_get_contents($v['tmp_name']);
  17.                         if (get_magic_quotes_runtime()) {
  18.                             $data = stripslashes($data);
  19.                         }
  20.                     }
  21.                     else if (isset($v['data'])) {
  22.                         $data = $v['data'];
  23.                     }
  24.                     if (!$data) {
  25.                         break;
  26.                     }
  27.                     $content_string .= '--' . $boundary . $crlf;
  28.                     $content_string .= 'Content-Disposition: form-data; name="' . $k . '"; filename="' . $v['name'] . '"' . $crlf;
  29.                     $content_string .= 'Content-Type: ' . $v['type'] . $crlf;
  30.                     if ($base64) {
  31.                         $content_string .= 'Content-Transfer-Encoding: base64' . $crlf . $crlf;
  32.                         $content_string .= chunk_split(base64_encode($data)) . $crlf;
  33.                     }
  34.                     else {
  35.                         $content_string .= 'Content-Transfer-Encoding: binary' . $crlf . $crlf;
  36.                         $content_string .= $data . $crlf;
  37.                     }
  38.                 }
  39.                 $content_string .= '--' . $boundary . '--' . $crlf;
  40.             }
  41.             else {
  42.                 $content_type = 'application/x-www-form-urlencoded';
  43.                 if ($args && is_array($args)) {
  44.                     $content_string = http_build_args($args);
  45.                 }
  46.             }
  47.  
  48.             $content_length = strlen($content_string);
  49.             $header_string="POST $path HTTP/1.0${crlf}Host: $hostaddr${crlf}User-Agent: $user_agent${crlf}Content-Type: $content_type${crlf}Content-Length: $content_length${crlf}";
  50.             break;

If the request is a POST, if the document has one or several attached files, writes a document of the type multipart/form-data, otherwise writes a document of the type application/x-www-form-urlencoded. In the first case, builds the body of the document with a first part which defines a unique separator then a series of secondary parts each containing one of the attached files encoded in base64. In the second case, builds the body of an HTTP document containing the parameters of the request, if any, properly formatted with http_build_args.

  1.         case 'GET':
  2.             if ($args && is_array($args)) {
  3.                 $path .= '?' . http_build_args($args);
  4.             }
  5.             $header_string="GET $path HTTP/1.0${crlf}Host: $hostaddr${crlf}User-Agent: $user_agent${crlf}";
  6.  
  7.  
  8.             break;

If the request is a GET, adds the parameters, if any, properly formatted with http_build_args after the access path, then builds the header of the HTTP document. The body of the document is empty.

  1.         default:
  2.             return false;
  3.     }

Returns false if $method isn't 'POST' or 'GET'.

  1.     if ($header && is_array($header)) {
  2.         foreach ($header as $name => $value) {
  3.             if (is_array($value)) {
  4.                 $value = implode('; ', $value);
  5.             }
  6.             $header_string .= "${name}: ${value}${crlf}";
  7.         }
  8.     }

Adds in $header_string the lignes defined by the array $header passed in argument.

  1.     $header_string .= "Connection: close${crlf}${crlf}";

Ends the header of the document.

  1.     return sendhttpraw($proto, $host, $portnum, $header_string, $content_string, $options);
  2. }

Sends $header_string and $content_string to $host with sendhttpraw and returns an array with the HTTP code sent back by the service, the MIME header of the document in an associative array and the plain content of the response.

sendhttpraw
SYNOPSIS

sendhttpraw($proto, $host, $portnum, $header_string, $content_string=false)

DESCRIPTION
CODE
  1. function sendhttpraw($proto, $host, $portnum, $header_string, $content_string=false, $options=false) {

sendhttpraw accepts 5 arguments: a connection type - tcp or ssl, a host name or an IP address, a port number, the header and the body of an HTTP document.

  1.     $url=$proto . '://' . $host . ':' . $portnum;
  2.  
  3.     $socket = $options ? @stream_socket_client($url, $errstr, $errno, 60, STREAM_CLIENT_CONNECT, stream_context_create($options)) : @stream_socket_client($url);
  4.  
  5.     if ($socket === false) {
  6.         return false;
  7.     }

Opens the connection with the server. Returns false in case of error.

  1.     if (fwrite($socket, $header_string) === false) {
  2.         return false;
  3.     }
  4.  
  5.     if ($content_string) {
  6.         $content_len = strlen($content_string);
  7.         for ($written = 0; $written < $content_len; $written += $w) {
  8.             $w = fwrite($socket, $written == 0 ? $content_string : substr($content_string, $written));
  9.             if ($w === false) {
  10.                 return false;
  11.             }
  12.         }
  13.     }

Writes the header of the document then the body of the document in several packets if necessary.

  1.     $response = '';
  2.     while (!feof($socket)) {
  3.         $response .= fread($socket, 8192);
  4.     }

Reads the response from the server.

  1.     fclose($socket);

Closes the connection with the server.

  1.     if (!$response) {
  2.         return false;
  3.     }

Returns false if the response is empty.

  1.     $crlf="\r\n";

Defines $crlf to the value of the end of line of MIME document.

  1.     list($response_headers, $response_body) = explode($crlf . $crlf, $response, 2);

Splits the header and the body of the document. Separates the header line by line. Extracts the first line of the header to get the return code.

  1.     $response_header_lines = explode($crlf, $response_headers);
  2.     $http_response_line = array_shift($response_header_lines);
  3.  
  4.     if (preg_match('@^HTTP/[0-9]\.[0-9] ([0-9]{3})@', $http_response_line, $r)) {
  5.         $response_code = $r[1];
  6.     }
  7.     else {
  8.         $response_code = 0;
  9.     }
  10.  
  11.     $response_header_array = array();
  12.     foreach ($response_header_lines as $header_line) {
  13.         list($header, $value) = explode(': ', $header_line, 2);
  14.         $response_header_array[$header] = $value;
  15.     }

Saves the header of the response in an associative array.

  1.     return array($response_code, $response_header_array, $response_body);
  2. }

Returns an array with the HTTP code sent back by the service, the MIME header of the document in an associative array and the plain content of the response.

sendget
SYNOPSIS

sendget($url, $args)

DESCRIPTION

sendget returns the document sent back by the HTTP service called with a GET at the address $url with the arguments $args.

sendget returns false in case of error.

CODE
  1. function sendget($url, $args=false, $options=false, $header=false) {
  2.     return sendhttp('GET', $url, $args, false, false, $options, $header);
  3. }

sendget returns the result of calling sendhttp with $method set to 'GET' and the arguments $url and $args.

sendpost
SYNOPSIS

sendpost($url, $args, $files=false, $base64=false)

DESCRIPTION

sendpost returns the document sent back by the HTTP service called with a POST at the address $url with the arguments $args and the attached $files encoded in base64 if $base64 is true.

sendpost returns false in case of error.

CODE
  1. function sendpost($url, $args=false, $files=false, $base64=false, $options=false, $header=false) {
  2.     return sendhttp('POST', $url, $args, $files, $base64, $options, $header);
  3. }

sendpost returns the result of calling sendhttp with $method set to 'POST' and the arguments $url, $args and $files and $base64.

http_build_args
SYNOPSIS

http_build_args($args)

DESCRIPTION

http_build_args returns the associative array $args in the form of a properly formatted URL string of parameters.

CODE
  1. function http_build_args($args) {
  2.     $args_string = '';
  3.  
  4.     foreach ($args as $name => $value) {
  5.         $args_string .= ($args_string ? '&' : '') . urlencode($name) . '=' . urlencode($value);
  6.     }
  7.  
  8.     return $args_string;
  9. }
http_parse_url
SYNOPSIS

http_parse_url($url)

DESCRIPTION

http_parse_url returns an array containing the protocol - tcp or ssl, the schema - http or https, the name of the host or the IP address, the port number and the path described by $url.

CODE
  1. function http_parse_url($url) {
  2.     $purl = @parse_url($url);
  3.     if ($purl === false) {
  4.         return false;
  5.     }
  6.  
  7.     $scheme = isset($purl['scheme']) ? $purl['scheme'] : 'http';
  8.     switch($scheme) {
  9.         case 'https':
  10.             $proto = 'ssl';
  11.             break;
  12.         case 'http':
  13.             $proto = 'tcp';
  14.             break;
  15.         default:
  16.             return false;
  17.     }
  18.     $host = isset($purl['host']) ? $purl['host'] : 'localhost';
  19.     $portnum = isset($purl['port']) ? $purl['port'] : ($scheme == 'https' ? 443 : 80);
  20.     $path = isset($purl['path']) ? $purl['path'] : '/';
  21.  
  22.     return array($proto, $scheme, $host, $portnum, $path);
  23. }
Application
Encoding a QR code

Google publishes an API for drawing charts and other images - Google Charts API - which can be used to obtain a PNG image of a QR code. Simply pass the proper parameters in an <img>:

<img src="http://chart.googleapis.com/chart?cht=qr&chf=bg,s,ffffff&chs=63x63&chld=M|0&chl=www.izend.org" />

To call this service in a program, wrap sendhttp in a specific function:

  1. require_once 'validatecolor.php';
  2. require_once 'phpqrcode/qrlib.php';
  3.  
  4. function qrencode($s, $size=1, $quality='M', $fg=false, $bg=false, $margin=0) {
  5.     $ql = array('L' => QR_ECLEVEL_L, 'M' => QR_ECLEVEL_M, 'Q' => QR_ECLEVEL_Q, 'H' => QR_ECLEVEL_H);
  6.  
  7.     if (!$s or !is_numeric($size) or !is_numeric($margin) or !$quality or !array_key_exists($quality, $ql) or ($fg and !validate_color($fg)) or ($bg and !validate_color($bg))) {
  8.         return false;
  9.     }
  10.  
  11.     if ($size < 1) {
  12.         $size=1;
  13.     }
  14.  
  15.     if ($margin < 0) {
  16.         $margin=0;
  17.     }
  18.  
  19.     $q=$ql[$quality];
  20.  
  21.     $frame = @QRcode::text($s, false, $q, 1, 0);
  22.  
  23.     if (!$frame) {
  24.         return false;
  25.     }
  26.  
  27.     $h = count($frame);
  28.     $w = strlen($frame[0]);
  29.  
  30.     $img = imagecreatetruecolor($w+2*$margin, $h);
  31.     $qrimg = imagecreatetruecolor($w*$size+2*$margin, $h*$size+2*$margin);
  32.  
  33.     if ($bg) {
  34.         $rgb=str_split($bg[0] == '#' ? substr($bg, 1, 6) : $bg, 2);
  35.         $r=hexdec($rgb[0]);
  36.         $g=hexdec($rgb[1]);
  37.         $b=hexdec($rgb[2]);
  38.     }
  39.     else {
  40.         $r=$g=$b=255;
  41.     }
  42.  
  43.     $bg=imagecolorallocate($img, $r, $g, $b);
  44.     imagefill($img, 0, 0, $bg);
  45.  
  46.     $bg=imagecolorallocate($qrimg, $r, $g, $b);
  47.     imagefill($qrimg, 0, 0, $bg);
  48.  
  49.     if ($fg) {
  50.         $rgb=str_split($fg[0] == '#' ? substr($fg, 1, 6) : $fg, 2);
  51.         $r=hexdec($rgb[0]);
  52.         $g=hexdec($rgb[1]);
  53.         $b=hexdec($rgb[2]);
  54.     }
  55.     else {
  56.         $r=$g=$b=0;
  57.     }
  58.  
  59.     $fg=imagecolorallocate($img, $r, $g, $b);
  60.  
  61.     for ($y=0; $y < $h; $y++) {
  62.         for ($x=0; $x < $w; $x++) {
  63.             if ($frame[$y][$x] == '1') {
  64.                 imagesetpixel($img, $x, $y, $fg);
  65.             }
  66.         }
  67.     }
  68.  
  69.     imagecopyresized($qrimg, $img, $margin, $margin, 0, 0, $w*$size, $h*$size, $w, $h);
  70.  
  71.     imagedestroy($img);
  72.  
  73.     ob_start();
  74.     imagepng($qrimg);
  75.     $png=ob_get_clean();
  76.     imagedestroy($qrimg);
  77.  
  78.     return $png;
  79. }

qrencode sends a GET request to the service at the address http://chart.googleapis.com/chart and returns the binary image data or false in case of error. $s contains the string of characters to be encoded. $size is in pixels. $quality can be either L, M, Q or H. See the documentation for all the details.

Decoding a QR code

Decoding the image of a QR code is more challenging. Suppose you haven't found a true HTTP API but just a form which lets you upload an image and returns the decoded text: ZXing Decoder Online. Try it. This form returns the decoded text or another HTML document in case of error.

Extract the source code of the form:

<form enctype="multipart/form-data" method="post" action="decode">
<p><input name="f" size="50" type="file"/>&nbsp;<input type="submit"/>
<input value="true" name="full" type="hidden"/></p></form>

Write a function which passes these parameters in an HTTP POST request:

  1. require_once 'sendhttp.php';
  2. require_once 'filemimetype.php';
  3.  
  4. function qrdecode($file) {
  5.     $url = 'http://zxing.org/w/decode';
  6.     $args = array(
  7.         'full'  => 'true',
  8.     );
  9.     $files=array('f' => array('name' => basename($file), 'tmp_name' => $file, 'type' => file_mime_type($file)));
  10.  
  11.     $response=sendpost($url, $args, $files, false); // DON'T encode data in base64
  12.  
  13.     if (!$response or $response[0] != 200) {
  14.         return false;
  15.     }
  16.  
  17.     if (!preg_match('#<tr><td>Parsed Result</td><td><pre.*>(.*)</pre></td></tr>#', $response[2], $r)) { // extract data - adapt when response format changes
  18.         return false;
  19.     }
  20.  
  21.     return strip_tags($r[1]);
  22. }

qrdecode sends a POST request at the address http://zxing.org/w/decode with an attached file. If the service returns another HTML document, qrdecode returns false.

IMPORTANT: Be very careful when inserting data returned by another program. If possible, remove all tags with strip_tags and add your own formatting.

Comments

To add a comment, click here.