Location: PHPKode > scripts > HTTP Headers Utility > http-headers-utility/HTTP_Headers_Util.php
<?php
// $Id: HTTP_Headers_Util.php,v 1.2 2003/01/22 12:25:30 k1m Exp $
// +----------------------------------------------------------------------+
// | HTTP Headers Util 0.1                                                |
// +----------------------------------------------------------------------+
// | Author: Keyvan Minoukadeh - hide@address.com - http://www.keyvan.net   |
// +----------------------------------------------------------------------+
// | Based on <http://search.cpan.org/author/GAAS/libwww-perl-5.65/lib/   |
// | HTTP/Headers/Util.pm>                                                |
// +----------------------------------------------------------------------+
// | This program is free software; you can redistribute it and/or        |
// | modify it under the terms of the GNU General Public License          |
// | as published by the Free Software Foundation; either version 2       |
// | of the License, or (at your option) any later version.               |
// |                                                                      |
// | This program is distributed in the hope that it will be useful,      |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
// | GNU General Public License for more details.                         |
// +----------------------------------------------------------------------+

if (!defined('HTTPNAV_ROOT')) define('HTTPNAV_ROOT', dirname(__FILE__).'/');
require_once(HTTPNAV_ROOT.'Debug.php');

/**
* HTTP Headers Util - Header value parsing utility functions
*
* This class is based on: 
* <http://search.cpan.org/author/GAAS/libwww-perl-5.65/lib/HTTP/Headers/Util.pm>
* by Gisle Aas.
* The text here is copied from the documentation of the above, obviously
* slightly modified as this is PHP not Perl. 
*
* SYNOPSIS
*
* $values = HTTP_Headers_Util::split_header_words($header_value);
*
* DESCRIPTION
* 
* This class provides a few functions that helps parsing and
* construction of valid HTTP header values.
* @static
*/
class HTTP_Headers_Util
{
    /**
    * split_header_words
    * 
    * This function will parse the header values given as argument into a
    * array containing key/value pairs.  The function
    * knows how to deal with ",", ";" and "=" as well as quoted values after
    * "=".  A list of space separated tokens are parsed as if they were
    * separated by ";".
    * 
    * If the $header_values passed as argument contains multiple values,
    * then they are treated as if they were a single value separated by
    * comma ",".
    * 
    * This means that this function is useful for parsing header fields that
    * follow this syntax (BNF as from the HTTP/1.1 specification, but we relax
    * the requirement for tokens).
    * 
    *   headers           = #header
    *   header            = (token | parameter) *( [";"] (token | parameter))
    * 
    *   token             = 1*<any CHAR except CTLs or separators>
    *   separators        = "(" | ")" | "<" | ">" | "@"
    *                     | "," | ";" | ":" | "\" | <">
    *                     | "/" | "[" | "]" | "?" | "="
    *                     | "{" | "}" | SP | HT
    * 
    *   quoted-string     = ( <"> *(qdtext | quoted-pair ) <"> )
    *   qdtext            = <any TEXT except <">>
    *   quoted-pair       = "\" CHAR
    * 
    *   parameter         = attribute "=" value
    *   attribute         = token
    *   value             = token | quoted-string
    * 
    * Each header is represented by an anonymous array of key/value
    * pairs.  The value for a simple token (not part of a parameter) is null.
    * Syntactically incorrect headers will not necessary be parsed as you
    * would want.
    * 
    * This is easier to describe with some examples:
    * 
    *    split_header_words('foo="bar"; port="80,81"; discard, bar=baz');
    *    split_header_words('text/html; charset="iso-8859-1");
    *    split_header_words('Basic realm="\"foo\\bar\""');
    * 
    * will return
    * 
    *    [foo=>'bar', port=>'80,81', discard=>null], [bar=>'baz']
    *    ['text/html'=>null, charset=>'iso-8859-1']
    *    [Basic=>null, realm=>'"foo\bar"']
    * 
    * @param mixed $header_values string or array
    * @return array
    * @static
    */
    function split_header_words($header_values)
    {
        if (!is_array($header_values)) $header_values = array($header_values);

        $result = array();
        foreach ($header_values as $header) {
            $cur = array();
            while ($header) {
                $key = '';
                $val = null;
                // 'token' or parameter 'attribute'
                if (preg_match('/^\s*(=*[^\s=;,]+)(.*)/', $header, $match)) {
                    $key = $match[1];
                    $header = $match[2];
                    // a quoted value
                    if (preg_match('/^\s*=\s*"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"(.*)/', $header, $match)) {
                        $val = $match[1];
                        $header = $match[2];
                        // remove backslash character escape
                        $val = preg_replace('/\\\\(.)/', "$1", $val);
                    // some unquoted value
                    } elseif (preg_match('/^\s*=\s*([^;,\s]*)(.*)/', $header, $match)) {
                        $val = trim($match[1]);
                        $header = $match[2];
                    }
                    // add details
                    $cur[$key] = $val;
                // reached the end, a new 'token' or 'attribute' about to start
                } elseif (preg_match('/^\s*,(.*)/', $header, $match)) {
                    $header = $match[1];
                    if (count($cur)) $result[] = $cur;
                    $cur = array();
                // continue
                } elseif (preg_match('/^\s*;(.*)/', $header, $match)) {
                    $header = $match[1];
                } elseif (preg_match('/^\s+(.*)/', $header, $match)) {
                    $header = $match[1];
                } else {
                    Debug::debug('This should not happen: "'.$header.'"', __FILE__, __LINE__);
                    return $result;
                }
            }
            if (count($cur)) $result[] = $cur;
        }
        return $result;
    }

    /*
    * join_header_words
    * 
    * This will do the opposite of the conversion done by split_header_words().
    * It takes a list of anonymous arrays as arguments (or a list of
    * key/value pairs) and produces a single header value.  Attribute values
    * are quoted if needed.
    * 
    * Example:
    * 
    *    join_header_words(array(array("text/plain" => null, "charset" => "iso-8859/1")));
    *    join_header_words(array("text/plain" => null, "charset" => "iso-8859/1"));
    * 
    * will both return the string:
    * 
    *    text/plain; charset="iso-8859/1"
    * 
    * @param array $header_values
    * @return string
    * @static
    */
    function join_header_words($header_values)
    {
        if (!is_array($header_values) || !count($header_values)) return false;
        if (!isset($header_values[0])) $header_values = array($header_values);

        $result = array();
        foreach ($header_values as $header) {
            $attr = array();
            foreach ($header as $key => $val) {
                if (isset($val)) {
                    if (preg_match('/^\w+$/', $val)) {
                        $key .= "=$val";
                    } else {
                        $val = preg_replace('/(["\\\\])/', "\\\\$1", $val);
                        $key .= "=\"$val\"";
                    }
                }
                $attr[] = $key;
            }
            if (count($attr)) $result[] = implode('; ', $attr);
        }
        return implode(', ', $result);
    }
}
Return current item: HTTP Headers Utility