Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
 
5
/**
6
 * HTTP
7
 *
8
 * PHP versions 4 and 5
9
 *
10
 * @category  HTTP
11
 * @package   HTTP
12
 * @author    Stig Bakken <ssb@fast.no>
13
 * @author    Sterling Hughes <sterling@php.net>
14
 * @author    Tomas V.V.Cox <cox@idecnet.com>
15
 * @author    Richard Heyes <richard@php.net>
16
 * @author    Philippe Jausions <jausions@php.net>
17
 * @author    Michael Wallner <mike@php.net>
18
 * @copyright 2002-2008 The Authors
19
 * @license   http://www.opensource.org/licenses/bsd-license.php  New BSD License
20
 * @version   CVS: $Id: HTTP.php,v 1.56 2008/08/31 20:15:43 jausions Exp $
21
 * @link      http://pear.php.net/package/HTTP
22
 */
23
 
24
/**
25
 * Miscellaneous HTTP Utilities
26
 *
27
 * PEAR::HTTP provides static shorthand methods for generating HTTP dates,
28
 * issueing HTTP HEAD requests, building absolute URIs, firing redirects and
29
 * negotiating user preferred language.
30
 *
31
 * @category HTTP
32
 * @package  HTTP
33
 * @author   Stig Bakken <ssb@fast.no>
34
 * @author   Sterling Hughes <sterling@php.net>
35
 * @author   Tomas V.V.Cox <cox@idecnet.com>
36
 * @author   Richard Heyes <richard@php.net>
37
 * @author   Philippe Jausions <jausions@php.net>
38
 * @author   Michael Wallner <mike@php.net>
39
 * @license  http://www.opensource.org/licenses/bsd-license.php  New BSD License
40
 * @abstract
41
 * @version  Release: $Revision: 1.56 $
42
 * @link     http://pear.php.net/package/HTTP
43
 */
44
class HTTP
45
{
46
    /**
47
     * Formats a RFC compliant GMT date HTTP header.  This function honors the
48
     * "y2k_compliance" php.ini directive and formats the GMT date corresponding
49
     * to either RFC850 or RFC822.
50
     *
51
     * @param mixed $time unix timestamp or date (default = current time)
52
     *
53
     * @return mixed  GMT date string, or false for an invalid $time parameter
54
     * @access public
55
     * @static
56
     */
57
    function Date($time = null)
58
    {
59
        if (!isset($time)) {
60
            $time = time();
61
        } elseif (!is_numeric($time) && (-1 === $time = strtotime($time))) {
62
            return false;
63
        }
64
 
65
        // RFC822 or RFC850
66
        $format = ini_get('y2k_compliance') ? 'D, d M Y' : 'l, d-M-y';
67
 
68
        return gmdate($format .' H:i:s \G\M\T', $time);
69
    }
70
 
71
    /**
72
     * Negotiates language with the user's browser through the Accept-Language
73
     * HTTP header or the user's host address.  Language codes are generally in
74
     * the form "ll" for a language spoken in only one country, or "ll-CC" for a
75
     * language spoken in a particular country.  For example, U.S. English is
76
     * "en-US", while British English is "en-UK".  Portugese as spoken in
77
     * Portugal is "pt-PT", while Brazilian Portugese is "pt-BR".
78
     *
79
     * Quality factors in the Accept-Language: header are supported, e.g.:
80
     *      Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
81
     *
82
     * <code>
83
     *  require_once 'HTTP.php';
84
     *  $langs = array(
85
     *      'en'    => 'locales/en',
86
     *      'en-US' => 'locales/en',
87
     *      'en-UK' => 'locales/en',
88
     *      'de'    => 'locales/de',
89
     *      'de-DE' => 'locales/de',
90
     *      'de-AT' => 'locales/de',
91
     *  );
92
     *  $neg = HTTP::negotiateLanguage($langs);
93
     *  $dir = $langs[$neg];
94
     * </code>
95
     *
96
     * @param array  $supported An associative array of supported languages,
97
     *                          whose values must evaluate to true.
98
     * @param string $default   The default language to use if none is found.
99
     *
100
     * @return string  The negotiated language result or the supplied default.
101
     * @static
102
     * @access public
103
     */
104
    function negotiateLanguage($supported, $default = 'en-US')
105
    {
106
        $supp = array();
107
        foreach ($supported as $lang => $isSupported) {
108
            if ($isSupported) {
109
                $supp[strtolower($lang)] = $lang;
110
            }
111
        }
112
 
113
        if (!count($supp)) {
114
            return $default;
115
        }
116
 
117
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
118
            $match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_LANGUAGE'],
119
                                        $supp);
120
            if (!is_null($match)) {
121
                return $match;
122
            }
123
        }
124
 
125
        if (isset($_SERVER['REMOTE_HOST'])) {
126
            $lang = strtolower(end($h = explode('.', $_SERVER['REMOTE_HOST'])));
127
            if (isset($supp[$lang])) {
128
                return $supp[$lang];
129
            }
130
        }
131
 
132
        return $default;
133
    }
134
 
135
    /**
136
     * Negotiates charset with the user's browser through the Accept-Charset
137
     * HTTP header.
138
     *
139
     * Quality factors in the Accept-Charset: header are supported, e.g.:
140
     *      Accept-Language: en-UK;q=0.7, en-US;q=0.6, no, dk;q=0.8
141
     *
142
     * <code>
143
     *  require_once 'HTTP.php';
144
     *  $charsets = array(
145
     *      'UTF-8',
146
     *      'ISO-8859-1',
147
     *  );
148
     *  $charset = HTTP::negotiateCharset($charsets);
149
     * </code>
150
     *
151
     * @param array  $supported An array of supported charsets
152
     * @param string $default   The default charset to use if none is found.
153
     *
154
     * @return string  The negotiated language result or the supplied default.
155
     * @static
156
     * @author Philippe Jausions <jausions@php.net>
157
     * @access public
158
     * @since  1.4.1
159
     */
160
    function negotiateCharset($supported, $default = 'ISO-8859-1')
161
    {
162
        $supp = array();
163
        foreach ($supported as $charset) {
164
            $supp[strtolower($charset)] = $charset;
165
        }
166
 
167
        if (!count($supp)) {
168
            return $default;
169
        }
170
 
171
        if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
172
            $match = HTTP::_matchAccept($_SERVER['HTTP_ACCEPT_CHARSET'],
173
                                        $supp);
174
            if (!is_null($match)) {
175
                return $match;
176
            }
177
        }
178
 
179
        return $default;
180
    }
181
 
182
    /**
183
     * Negotiates content type with the user's browser through the Accept
184
     * HTTP header.
185
     *
186
     * Quality factors in the Accept: header are supported, e.g.:
187
     *      Accept: application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8
188
     *
189
     * <code>
190
     *  require_once 'HTTP.php';
191
     *  $contentType = array(
192
     *      'application/xhtml+xml',
193
     *      'application/xml',
194
     *      'text/html',
195
     *      'text/plain',
196
     *  );
197
     *  $mime = HTTP::negotiateContentType($contentType);
198
     * </code>
199
     *
200
     * @param array  $supported An associative array of supported MIME types.
201
     * @param string $default   The default type to use if none match.
202
     *
203
     * @return string  The negotiated MIME type result or the supplied default.
204
     * @static
205
     * @author Philippe Jausions <jausions@php.net>
206
     * @access public
207
     * @since  1.4.1
208
     */
209
    function negotiateMimeType($supported, $default)
210
    {
211
        $supp = array();
212
        foreach ($supported as $type) {
213
            $supp[strtolower($type)] = $type;
214
        }
215
 
216
        if (!count($supp)) {
217
            return $default;
218
        }
219
 
220
        if (isset($_SERVER['HTTP_ACCEPT'])) {
221
            $accepts = HTTP::_sortAccept($_SERVER['HTTP_ACCEPT']);
222
 
223
            foreach ($accepts as $type => $q) {
224
                if (substr($type, -2) != '/*') {
225
                    if (isset($supp[$type])) {
226
                        return $supp[$type];
227
                    }
228
                    continue;
229
                }
230
                if ($type == '*/*') {
231
                    return array_shift($supp);
232
                }
233
                list($general, $specific) = explode('/', $type);
234
                $general .= '/';
235
                $len = strlen($general);
236
                foreach ($supp as $mime => $t) {
237
                    if (strncasecmp($general, $mime, $len) == 0) {
238
                        return $t;
239
                    }
240
                }
241
            }
242
        }
243
 
244
        return $default;
245
    }
246
 
247
    /**
248
     * Parses a weighed "Accept" HTTP header and matches it against a list
249
     * of supported options
250
     *
251
     * @param string  $header    The HTTP "Accept" header to parse
252
     * @param array   $supported A list of supported values
253
     *
254
     * @return string|NULL  a matched option, or NULL if no match
255
     * @access private
256
     * @static
257
     */
258
    function _matchAccept($header, $supported)
259
    {
260
        $matches = HTTP::_sortAccept($header);
261
        foreach ($matches as $key => $q) {
262
            if (isset($supported[$key])) {
263
                return $supported[$key];
264
            }
265
        }
266
        // If any (i.e. "*") is acceptable, return the first supported format
267
        if (isset($matches['*'])) {
268
            return array_shift($supported);
269
        }
270
        return null;
271
    }
272
 
273
    /**
274
     * Parses and sorts a weighed "Accept" HTTP header
275
     *
276
     * @param string  $header The HTTP "Accept" header to parse
277
     *
278
     * @return array  a sorted list of "accept" options
279
     * @access private
280
     * @static
281
     */
282
    function _sortAccept($header)
283
    {
284
        $matches = array();
285
        foreach (explode(',', $header) as $option) {
286
            $option = array_map('trim', explode(';', $option));
287
 
288
            $l = strtolower($option[0]);
289
            if (isset($option[1])) {
290
                $q = (float) str_replace('q=', '', $option[1]);
291
            } else {
292
                $q = null;
293
                // Assign default low weight for generic values
294
                if ($l == '*/*') {
295
                    $q = 0.01;
296
                } elseif (substr($l, -1) == '*') {
297
                    $q = 0.02;
298
                }
299
            }
300
            // Unweighted values, get high weight by their position in the
301
            // list
302
            $matches[$l] = isset($q) ? $q : 1000 - count($matches);
303
        }
304
        arsort($matches, SORT_NUMERIC);
305
        return $matches;
306
    }
307
 
308
    /**
309
     * Sends a "HEAD" HTTP command to a server and returns the headers
310
     * as an associative array.
311
     *
312
     * Example output could be:
313
     * <code>
314
     *     Array
315
     *     (
316
     *         [response_code] => 200          // The HTTP response code
317
     *         [response] => HTTP/1.1 200 OK   // The full HTTP response string
318
     *         [Date] => Fri, 11 Jan 2002 01:41:44 GMT
319
     *         [Server] => Apache/1.3.20 (Unix) PHP/4.1.1
320
     *         [X-Powered-By] => PHP/4.1.1
321
     *         [Connection] => close
322
     *         [Content-Type] => text/html
323
     *     )
324
     * </code>
325
     *
326
     * @param string  $url     A valid URL, e.g.: http://pear.php.net/credits.php
327
     * @param integer $timeout Timeout in seconds (default = 10)
328
     *
329
     * @return array  Returns associative array of response headers on success
330
     *                or PEAR error on failure.
331
     * @static
332
     * @access public
333
     * @see HTTP_Client::head()
334
     * @see HTTP_Request
335
     */
336
    function head($url, $timeout = 10)
337
    {
338
        $p = parse_url($url);
339
        if (!isset($p['scheme'])) {
340
            $p = parse_url(HTTP::absoluteURI($url));
341
        } elseif ($p['scheme'] != 'http') {
342
            return HTTP::raiseError('Unsupported protocol: '. $p['scheme']);
343
        }
344
 
345
        $port = isset($p['port']) ? $p['port'] : 80;
346
 
347
        if (!$fp = @fsockopen($p['host'], $port, $eno, $estr, $timeout)) {
348
            return HTTP::raiseError("Connection error: $estr ($eno)");
349
        }
350
 
351
        $path  = !empty($p['path']) ? $p['path'] : '/';
352
        $path .= !empty($p['query']) ? '?' . $p['query'] : '';
353
 
354
        fputs($fp, "HEAD $path HTTP/1.0\r\n");
355
        fputs($fp, 'Host: ' . $p['host'] . ':' . $port . "\r\n");
356
        fputs($fp, "Connection: close\r\n\r\n");
357
 
358
        $response = rtrim(fgets($fp, 4096));
359
        if (preg_match("|^HTTP/[^\s]*\s(.*?)\s|", $response, $status)) {
360
            $headers['response_code'] = $status[1];
361
        }
362
        $headers['response'] = $response;
363
 
364
        while ($line = fgets($fp, 4096)) {
365
            if (!trim($line)) {
366
                break;
367
            }
368
            if (($pos = strpos($line, ':')) !== false) {
369
                $header = substr($line, 0, $pos);
370
                $value  = trim(substr($line, $pos + 1));
371
 
372
                $headers[$header] = $value;
373
            }
374
        }
375
        fclose($fp);
376
        return $headers;
377
    }
378
 
379
    /**
380
     * This function redirects the client. This is done by issuing
381
     * a "Location" header and exiting if wanted.  If you set $rfc2616 to true
382
     * HTTP will output a hypertext note with the location of the redirect.
383
     *
384
     * @param string $url     URL where the redirect should go to.
385
     * @param bool   $exit    Whether to exit immediately after redirection.
386
     * @param bool   $rfc2616 Wheter to output a hypertext note where we're
387
     *                        redirecting to (Redirecting to
388
     *                        <a href="...">...</a>.)
389
     *
390
     * @return boolean  Returns TRUE on succes (or exits) or FALSE if headers
391
     *                  have already been sent.
392
     * @static
393
     * @access public
394
     */
395
    function redirect($url, $exit = true, $rfc2616 = false)
396
    {
397
        if (headers_sent()) {
398
            return false;
399
        }
400
 
401
        $url = HTTP::absoluteURI($url);
402
        header('Location: '. $url);
403
 
404
        if ($rfc2616 && isset($_SERVER['REQUEST_METHOD'])
405
            && $_SERVER['REQUEST_METHOD'] != 'HEAD') {
406
            echo '
407
<p>Redirecting to: <a href="'.str_replace('"', '%22', $url).'">'
408
                 .htmlspecialchars($url).'</a>.</p>
409
<script type="text/javascript">
410
//<![CDATA[
411
if (location.replace == null) {
412
    location.replace = location.assign;
413
}
414
location.replace("'.str_replace('"', '\\"', $url).'");
415
// ]]>
416
</script>';
417
        }
418
        if ($exit) {
419
            exit;
420
        }
421
        return true;
422
    }
423
 
424
    /**
425
     * This function returns the absolute URI for the partial URL passed.
426
     * The current scheme (HTTP/HTTPS), host server, port, current script
427
     * location are used if necessary to resolve any relative URLs.
428
     *
429
     * Offsets potentially created by PATH_INFO are taken care of to resolve
430
     * relative URLs to the current script.
431
     *
432
     * You can choose a new protocol while resolving the URI.  This is
433
     * particularly useful when redirecting a web browser using relative URIs
434
     * and to switch from HTTP to HTTPS, or vice-versa, at the same time.
435
     *
436
     * @param string  $url      Absolute or relative URI the redirect should
437
     *                          go to.
438
     * @param string  $protocol Protocol to use when redirecting URIs.
439
     * @param integer $port     A new port number.
440
     *
441
     * @return string  The absolute URI.
442
     * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
443
     * @static
444
     * @access public
445
     */
446
    function absoluteURI($url = null, $protocol = null, $port = null)
447
    {
448
        // filter CR/LF
449
        $url = str_replace(array("\r", "\n"), ' ', $url);
450
 
451
        // Mess around protocol and port with already absolute URIs
452
        if (preg_match('!^([a-z0-9]+)://!i', $url)) {
453
            if (empty($protocol) && empty($port)) {
454
                return $url;
455
            }
456
            if (!empty($protocol)) {
457
                $url = $protocol .':'. end($array = explode(':', $url, 2));
458
            }
459
            if (!empty($port)) {
460
                $url = preg_replace('!^(([a-z0-9]+)://[^/:]+)(:[\d]+)?!i',
461
                                    '\1:'. $port, $url);
462
            }
463
            return $url;
464
        }
465
 
466
        $host = 'localhost';
467
        if (!empty($_SERVER['HTTP_HOST'])) {
468
            list($host) = explode(':', $_SERVER['HTTP_HOST']);
469
        } elseif (!empty($_SERVER['SERVER_NAME'])) {
470
            list($host) = explode(':', $_SERVER['SERVER_NAME']);
471
        }
472
 
473
        if (empty($protocol)) {
474
            if (isset($_SERVER['HTTPS']) && !strcasecmp($_SERVER['HTTPS'], 'on')) {
475
                $protocol = 'https';
476
            } else {
477
                $protocol = 'http';
478
            }
479
            if (!isset($port) || $port != intval($port)) {
480
                $port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
481
            }
482
        }
483
 
484
        if ($protocol == 'http' && $port == 80) {
485
            unset($port);
486
        }
487
        if ($protocol == 'https' && $port == 443) {
488
            unset($port);
489
        }
490
 
491
        $server = $protocol.'://'.$host.(isset($port) ? ':'.$port : '');
492
 
493
        $uriAll = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI']
494
                                                 : $_SERVER['PHP_SELF'];
495
        if (false !== ($q = strpos($uriAll, '?'))) {
496
            $uriBase = substr($uriAll, 0, $q);
497
        } else {
498
            $uriBase = $uriAll;
499
        }
500
        if (!strlen($url) || $url{0} == '#') {
501
            $url = $uriAll.$url;
502
        } elseif ($url{0} == '?') {
503
            $url = $uriBase.$url;
504
        }
505
        if ($url{0} == '/') {
506
            return $server . $url;
507
        }
508
 
509
        // Adjust for PATH_INFO if needed
510
        if (isset($_SERVER['PATH_INFO']) && strlen($_SERVER['PATH_INFO'])) {
511
            $path = dirname(substr($uriBase, 0,
512
                                   -strlen($_SERVER['PATH_INFO'])));
513
        } else {
514
            /**
515
             * Fixes bug #12672 PHP_SELF ending on / causes incorrect redirects
516
             *
517
             * @link http://pear.php.net/bugs/12672
518
             */
519
            $path = dirname($uriBase.'-');
520
        }
521
 
522
        if (substr($path = strtr($path, '\\', '/'), -1) != '/') {
523
            $path .= '/';
524
        }
525
 
526
        return $server . $path . $url;
527
    }
528
 
529
    /**
530
     * Raise Error
531
     *
532
     * Lazy raising of PEAR_Errors.
533
     *
534
     * @param mixed   $error Error
535
     * @param integer $code  Error code
536
     *
537
     * @return object  PEAR_Error
538
     * @static
539
     * @access protected
540
     */
541
    function raiseError($error = null, $code = null)
542
    {
543
        include_once 'PEAR.php';
544
        return PEAR::raiseError($error, $code);
545
    }
546
}
547
 
548
?>