13

I am using PHP 5.3.5 on windows, and I didn't find any pecl_http.dll that works with my installation.

So my question is,

How do I get the functionality of http_parse_headers without using PECL?

hakre
  • 193,403
  • 52
  • 435
  • 836
Shrinath
  • 7,888
  • 13
  • 48
  • 85
  • And no, I don't have visual studio either, incase you ask me to build it :( – Shrinath Jun 16 '11 at 07:38
  • use curl. [this is a working snippet][1] of code that replaces httpRequest. [1]: http://stackoverflow.com/a/31318117/1166727 – tony gil Jul 09 '15 at 20:37

8 Answers8

16

From the documentation page, first comment:

 if( !function_exists( 'http_parse_headers' ) ) {
     function http_parse_headers( $header )
     {
         $retVal = array();
         $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header));
         foreach( $fields as $field ) {
             if( preg_match('/([^:]+): (.+)/m', $field, $match) ) {
                 $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1])));
                 if( isset($retVal[$match[1]]) ) {
                     $retVal[$match[1]] = array($retVal[$match[1]], $match[2]);
                 } else {
                     $retVal[$match[1]] = trim($match[2]);
                 }
             }
         }
         return $retVal;
     }
}

Alternatively, you may want to read how to install a PECL extension on Windows, to be honest, I don't know anything about that.

Berry Langerak
  • 18,561
  • 4
  • 45
  • 58
  • Keeping the thread open just to see if there are any alternative suggestions lying around there... – Shrinath Jun 16 '11 at 08:00
  • 1
    oh wait, I see this too : "it's probably not as robust as the PECL version, as it only parses if the headers are separated by newlines (\n)." in the same page!! :( – Shrinath Jun 16 '11 at 08:03
  • @Shrinath That is another example, so does not apply to this function. Have you actually tried using it? It think it actually works the same as http_parse_headers, although it might be a bit different. – Berry Langerak Jun 16 '11 at 08:19
  • @Berry Langerak : Sorry dude, the @Gordon guy takes it for that link :) Which actually solved my purpose, even though that wasn't the actual question. – Shrinath Jun 16 '11 at 09:11
  • 1
    @Shrinath No worries. :) If that helps you, it helps you! – Berry Langerak Jun 16 '11 at 09:18
  • This uses the deprecated /e modifier. – markus Nov 20 '13 at 21:17
10

This is also lifted from the PHP Documentation for http_parse_headers. When I compared it with @Berry Langerak's answer (using microtime) I found it to be 17% faster on average, using a sample of 10 headers (presumably because it doesn't use regular expressions).

if (!function_exists('http_parse_headers')) {
    function http_parse_headers($raw_headers) {
        $headers = array();
        $key = '';

        foreach(explode("\n", $raw_headers) as $i => $h) {
            $h = explode(':', $h, 2);

            if (isset($h[1])) {
                if (!isset($headers[$h[0]]))
                    $headers[$h[0]] = trim($h[1]);
                elseif (is_array($headers[$h[0]])) {
                    $headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1])));
                }
                else {
                    $headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1])));
                }

                $key = $h[0];
            }
            else { 
                if (substr($h[0], 0, 1) == "\t")
                    $headers[$key] .= "\r\n\t".trim($h[0]);
                elseif (!$key) 
                    $headers[0] = trim($h[0]); 
            }
        }
        
        return $headers;
    }
}

Note: this includes the fix of the small mistake the author points out in a later comment.


Regex Function

0.00035881996
0.00036096572
0.00034999847
0.00043797492
0.00033497810

Average: 0.000368547434

This Function

0.00006198883
0.00006604194
0.00007104873
0.00006413459
0.00006389617

Average 0.000065422052

Community
  • 1
  • 1
Red Taz
  • 4,159
  • 4
  • 38
  • 60
  • And most importantly it does use the deprecated /e modifier which doesn't fly in latest php versions. Thanks! – sfscs Jun 01 '18 at 23:36
9

You can get the extension for Windows at

It's one of the php_http-5.3-*-x86.zip files. Check which PHP you have installed and pick the right one, e.g. my PHP is a php-5.3.6-nts-Win32-VC9-x86, so I needed php_http-5.3-nts-svn20091125-vc9-x86.zip.

Download the zip and extract the php_http.dll to your extension folder. The extension folder should be the /ext folder in your php installation directory. If you are not sure, open your php.ini file and search for these lines:

; Directory in which the loadable extensions (modules) reside.
; http://php.net/extension-dir
; extension_dir = "./"
; On windows:
extension_dir = .\ext

The value for extension_dir is where you have to place the dll. If you are unsure where your php.ini is located, open a command prompt and do

php --ini

This will tell you where your php.ini is. It will output something like

Configuration File (php.ini) Path: C:\Windows
Loaded Configuration File:         C:\php5\php-5.3.6-nts-Win32-VC9-x86\php.ini
Scan for additional .ini files in: (none)
Additional .ini files parsed:      (none)

After you have copied the dll, add the extension to your php.ini to enable it. Find where it says something like

;;;;;;;;;;;;;;;;;;;;;;
; Dynamic Extensions ;
;;;;;;;;;;;;;;;;;;;;;;

There should be multiple lines similar to this:

;extension=php_sybase_ct.dll
extension=php_tidy.dll
;extension=php_xmlrpc.dll
extension=php_xsl.dll
;extension=php_zip.dll

Add the following line:

extension=php_http.dll

Save the php.ini and type the following at your command prompt:

php --ri http

You should now get a rather extensive output starting with

http
HTTP Support => enabled
Extension Version => 1.7.0-dev
… more stuff

This means, you have successfully installed the extension and can use it now.

Note, that in order to be able to load this extension on Windows, you additionally need to load the following PHP extensions: hash, iconv and SPL.

Gordon
  • 312,688
  • 75
  • 539
  • 559
  • (1). Hash,iconv,SPL -> I don't know what they are, I didn't do anything about it. (2). It worked :) I didn't know about that link. Just had to download the required dll and do the extension part. Thanks – Shrinath Jun 16 '11 at 09:10
  • @Shrinath Hash, Iconv and SPL are additional extensions. IIRC they are enabled by default in PHP5.3, so that's why you didnt have to do anything about them. I just felt I should note it for completeness. – Gordon Jun 16 '11 at 09:14
  • 1
    I like that "note it for completeness" part :) Thanks :) – Shrinath Jun 16 '11 at 09:21
  • I need your help, After I Installed the dll the apache keeps telling me: php5.dll is missing from your computer plz response – Khaleel Hmoz Aug 14 '11 at 08:45
  • Technically doesn't answer the question, "How do I ... *without using PECL*?". But probably the "right" answer here. – rvighne Jan 20 '14 at 05:49
2

Since I could not find any function that behaved exactly like the PECL once, I wrote my own, compared to the others it turns out to be pretty fast…

function dirtyHeaderParser($headers, $strict = true){
    $arr = array();
    $s = strtok($headers, ':');
    while ($s){
        if ( ($s[0] === ' ') || ($s[0] === "\t") ){
            if (count($arr) != 0){
                $tail = strtok('');
                $tail = "{$s}:{$tail}";
                $v = strtok($tail, "\n");
                if (is_array($arr[$key])){
                    end($arr[$key]);
                    $last = key($arr[$key]);
                    $arr[$key][$last] = "{$arr[$key][$last]}\n{$v}";
                    reset($arr[$key]);
                } else {
                    $arr[$key] = "{$arr[$key]}\n{$v}";
                }
            }
        } else {
            $v = strtok("\n");
            if ($v){
                $key = strtolower($s);
                if (((strpos($key, "\n") !== false) || (strpos($key, "\t") !== false) || (strpos($key, " ") !== false)) && $strict) {
                    return false;
                } 
                if (array_key_exists($key, $arr)){
                    if (!is_array($arr[$key])){
                        $arr[$key] = array($arr[$key]);
                    }
                    $arr[$key][] = trim($v);
                } else {
                    $arr[$key] = trim($v);
                }
            } else {
                break;
            }
        }
        $s = strtok(':');
    }
    return (count($arr) == 0) ? false : $arr;
}

Strict mode means, it will return false if a header key contains \n, whitespace or \t. It supports multiple line headers and double header values (with multiple lines too), if anyone finds something not behaving like the PECL version, I would be happy if you leave a comment.

ePirat
  • 1,068
  • 11
  • 20
1

Hate to continue such an old thread that's already so long, but this one is superior because:

  • Doesn't use regexps
  • Handles duplicate headers correctly
  • Not so many array and string mutation calls
  • Parses the Status line

(You may need code to check if the extension already exists)

function http_parse_headers ($raw) {
    $res = [];
    foreach (explode("\n", $raw) as $h) {
        $h = explode(':', $h, 2);
        $first = trim($h[0]);
        $last = trim($h[1]);
        if (array_key_exists($first, $res)) {
            $res[$first] .= ", " . $last;
        } else if (isset($h[1])) {
            $res[$first] = $last;
        } else {
            $res[] = $first;
        }
    }
    return $res;
}
rvighne
  • 20,755
  • 11
  • 51
  • 73
  • I realize this is an old answer, but for anyone coming across it (as I just did), note: Any replacement function that does not *exactly* mimic the behavior of the original pecl function (including bugs and oversights) is not "superior." – alzee Dec 16 '16 at 15:26
1
function parse_headers($headers)
{
    $headers = preg_replace('/^\r\n/m', '', $headers);
    $headers = preg_replace('/\r\n\s+/m', ' ', $headers);
    preg_match_all('/^([^: ]+):\s(.+?(?:\r\n\s(?:.+?))*)?\r\n/m', $headers . "\r\n", $matches);

    $result = array();
    foreach ($matches[1] as $key => $value)
        $result[$value] = (array_key_exists($value, $result) ? $result[$value] . "\n" : '') . $matches[2][$key];

    return $result;
}
1

Here is a modified version form the documentation page which works just like the PECL version :)

function http_parse_headers( $header ) {
        $retVal = array();
        $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header));
        foreach( $fields as $field ) {
            if( preg_match('/([^:]+): (.+)/m', $field, $match) ) {
                $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1])));
                if( isset($retVal[$match[1]]) ) {
                    if ( is_array( $retVal[$match[1]] ) ) {
                        $i = count($retVal[$match[1]]);
                        $retVal[$match[1]][$i] = $match[2];
                    }
                    else {
                        $retVal[$match[1]] = array($retVal[$match[1]], $match[2]);
                    }
                } else {
                    $retVal[$match[1]] = trim($match[2]);
                }
            }
        }
        return $retVal;
    }
Ej52
  • 11
  • 1
0

Unfortunately, for the function provided in the documentation page, if there are more than two of the same header (ie Set-Cookie), the structure of the array will be incorrect, eg:

    [Set-Cookie] => Array
    (
        [0] => Array
        (
            [0] => AWESOMESESSIONID=xQ5TRl1GXDQcQCXytfb1PK!-744872953!NONE; Path=/cte-lps2s-test2/; Secure
            [1] => AWESOME_SESSH_c25c28d0-b763-11df-979f23a029aa77=%2Fcte-lps2s-test2; Path=/
        ) 
        [1] => MOREAWESOME=2_uGgNpo2-jm-CjfaefLzjFhmmP-y3HzwNZKE0gsTeP+; Path=/; Secure
    )

The below modification to the code will fix this error (see the comment):

if( !function_exists( 'http_parse_headers' ) ) {
     function http_parse_headers( $header )
     {
         $retVal = array();
         $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ',$header));
         foreach( $fields as $field ) {
             if( preg_match('/([^:]+): (.+)/m', $field, $match) ) {
                 $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")',     strtolower(trim($match[1])));
                 // We need to check if an array of values has already been created, to     maintain the correct level in the $retVal array.
                 if(is_array($retVal[$match[1]])) {
                        $retVal[$match[1]] []= $match[2];
                 }
                 else {
                    $retVal[$match[1]] = array($retVal[$match[1]], $match[2]);
                 }
             } else {
                 $retVal[$match[1]] = trim($match[2]);
             }
         }
         return $retVal;
     }
}
Brian Duncan
  • 1,176
  • 1
  • 13
  • 21
  • This uses the deprecated /e modifier. – markus Nov 20 '13 at 21:18
  • @markus are you a bot that comments on all examples of deprecated code on stackoverflow? You really have your work cut out for you. So replace the above with preg_replace_callback() like the docs say. Things change. – Brian Duncan Nov 26 '13 at 18:59
  • Why do I need to be a bot to point out deprecated code samples? This is important information that should either go into the answer or prompt the author of the answer to adapt or delete. – markus Nov 26 '13 at 21:11