Completed
Push — misc20160411 ( 877f9b )
by Richard
07:29
created

HttpRequest::getDomain()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
cc 2
eloc 4
nc 2
nop 0
crap 2
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
 */
11
12
namespace Xoops\Core;
13
14
/**
15
 * WARNING: THIS IS A PLACEHOLDER ONLY. IMPLEMENTATION AND DETAILS WILL CHANGE.
16
 *
17
 * This provides some of the functionality that was in the Xoops_Request classes.
18
 * The majority of use for the class was the 'asXyz()' methods, and all such uses
19
 * should move to Xmf\Request::getXyz() methods.
20
 *
21
 * These are methods which reveal some aspects of the HTTP request environment.
22
 * This will eventually be reworked to depend on a full HTTP message library
23
 * (anticipating an official PSR-7 implementation.)
24
 *
25
 * For now, this is a reduced version of a Cake derivative.
26
 *
27
 */
28
29
/**
30
 * HttpRequest
31
 *
32
 * @category  Xoops\Core\HttpRequest
33
 * @package   Xoops\Core
34
 * @author    trabis <[email protected]>
35
 * @author    Kazumi Ono <[email protected]>
36
 * @author    Richard Griffith <[email protected]>
37
 * @copyright 2011-2015 XOOPS Project (http://xoops.org)
38
 * @license   GNU GPL 2 or later (http://www.gnu.org/licenses/gpl-2.0.html)
39
 * @link      http://xoops.org
40
 */
41
class HttpRequest
42
{
43
    /**
44
     * @var array
45
     */
46
    protected $params;
47
48
    /**
49
     * @var HttpRequest The reference to *Singleton* instance of this class
50
     */
51
    private static $instance;
52
53
    /**
54
     * The built in detectors used with `is()` can be modified with `addDetector()`.
55
     * There are several ways to specify a detector, see HttpRequest::addDetector() for the
56
     * various formats and ways to define detectors.
57
     *
58
     * @var array
59
     */
60
    protected $detectors = array(
61
        'get'       => array('env' => 'REQUEST_METHOD', 'value' => 'GET'),
62
        'post'      => array('env' => 'REQUEST_METHOD', 'value' => 'POST'),
63
        'put'       => array('env' => 'REQUEST_METHOD', 'value' => 'PUT'),
64
        'delete'    => array('env' => 'REQUEST_METHOD', 'value' => 'DELETE'),
65
        'head'      => array('env' => 'REQUEST_METHOD', 'value' => 'HEAD'),
66
        'options'   => array('env' => 'REQUEST_METHOD', 'value' => 'OPTIONS'),
67
        'safemethod'=> array('env' => 'REQUEST_METHOD', 'options' => array('GET', 'HEAD')),
68
        'ssl'       => array('env' => 'HTTPS', 'value' => 1),
69
        'ajax'      => array('env' => 'HTTP_X_REQUESTED_WITH', 'value' => 'XMLHttpRequest'),
70
        'flash'     => array('env' => 'HTTP_USER_AGENT', 'pattern' => '/^(Shockwave|Adobe) Flash/'),
71
        'mobile'    => array(
72
            'env'     => 'HTTP_USER_AGENT',
73
            'options' => array(
74
                'Android',
75
                'AvantGo',
76
                'BlackBerry',
77
                'DoCoMo',
78
                'Fennec',
79
                'iPod',
80
                'iPhone',
81
                'iPad',
82
                'J2ME',
83
                'MIDP',
84
                'NetFront',
85
                'Nokia',
86
                'Opera Mini',
87
                'Opera Mobi',
88
                'PalmOS',
89
                'PalmSource',
90
                'portalmmm',
91
                'Plucker',
92
                'ReqwirelessWeb',
93
                'SonyEricsson',
94
                'Symbian',
95
                'UP\\.Browser',
96
                'webOS',
97
                'Windows CE',
98
                'Windows Phone OS',
99
                'Xiino'
100
            )
101
        ),
102
        'robot'     => array(
103
            'env'     => 'HTTP_USER_AGENT',
104
            'options' => array(
105
                /* The most common ones. */
106
                'Googlebot',
107
                'msnbot',
108
                'Slurp',
109
                'Yahoo',
110
                /* The rest alphabetically. */
111
                'Arachnoidea',
112
                'ArchitextSpider',
113
                'Ask Jeeves',
114
                'B-l-i-t-z-Bot',
115
                'Baiduspider',
116
                'BecomeBot',
117
                'cfetch',
118
                'ConveraCrawler',
119
                'ExtractorPro',
120
                'FAST-WebCrawler',
121
                'FDSE robot',
122
                'fido',
123
                'geckobot',
124
                'Gigabot',
125
                'Girafabot',
126
                'grub-client',
127
                'Gulliver',
128
                'HTTrack',
129
                'ia_archiver',
130
                'InfoSeek',
131
                'kinjabot',
132
                'KIT-Fireball',
133
                'larbin',
134
                'LEIA',
135
                'lmspider',
136
                'Lycos_Spider',
137
                'Mediapartners-Google',
138
                'MuscatFerret',
139
                'NaverBot',
140
                'OmniExplorer_Bot',
141
                'polybot',
142
                'Pompos',
143
                'Scooter',
144
                'Teoma',
145
                'TheSuBot',
146
                'TurnitinBot',
147
                'Ultraseek',
148
                'ViolaBot',
149
                'webbandit',
150
                'www\\.almaden\\.ibm\\.com\\/cs\\/crawler',
151
                'ZyBorg',
152
            )
153
        ),
154
    );
155
156
    /**
157
     * __construct
158
     */
159
    private function __construct()
160
    {
161
        switch (strtolower($this->getEnv('REQUEST_METHOD'))) {
162
            case 'get':
163
                $params = $_GET;
164
                break;
165
            case 'put':
166
                parse_str(file_get_contents('php://input'), $put);
167
                $params = array_merge($_GET, $put);
168
                break;
169
            default:
170
                $params = array_merge($_GET, $_POST);
171
        }
172
        $this->params = $params;
173
    }
174
175
    /**
176
     * get singleton instance, establish the request data on first access
177
     *
178
     * @return HttpRequest
179
     */
180 11
    public static function getInstance()
181
    {
182 11
        if (null === static::$instance) {
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
183
            static::$instance = new static();
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
184
        }
185 11
        return static::$instance;
0 ignored issues
show
Bug introduced by
Since $instance is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $instance to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
186
    }
187
188
    /**
189
     * get a http header for the current request
190
     *
191
     * @param null|string $name header name
192
     *
193
     * @return null|string
194
     */
195
    public function getHeader($name = null)
196
    {
197
        if ($name === null) {
198
            return $name;
199
        }
200
201
        // Try to get it from the $_SERVER array first
202
        if ($res = $this->getEnv('HTTP_' . strtoupper(str_replace('-', '_', $name)))) {
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getEnv('HTTP_' . ...ace('-', '_', $name))); of type string|boolean adds the type boolean to the return on line 203 which is incompatible with the return type documented by Xoops\Core\HttpRequest::getHeader of type null|string.
Loading history...
203
            return $res;
204
        }
205
206
        // This seems to be the only way to get the Authorization header on
207
        // Apache
208
        if (function_exists('apache_request_headers')) {
209
            $headers = apache_request_headers();
210
            if (!empty($headers[$name])) {
211
                return $headers[$name];
212
            }
213
        }
214
        return null;
215
    }
216
217
    /**
218
     * get the scheme of current request
219
     *
220
     * @return string
221
     */
222 1
    public function getScheme()
223
    {
224 1
        return $this->getEnv('HTTPS') ? 'https' : 'http';
225
    }
226
227
    /**
228
     * get the host from the current request
229
     *
230
     * @return string
231
     */
232 1
    public function getHost()
233
    {
234 1
        return $this->getEnv('HTTP_HOST') ? (string) $this->getEnv('HTTP_HOST') : 'localhost';
235
    }
236
237
    /**
238
     * get the URI of the current request
239
     *
240
     * @return null|string
241
     */
242
    public static function getUri()
243
    {
244
        if (empty($_SERVER['PHP_SELF']) || empty($_SERVER['REQUEST_URI'])) {
245
            // IIS
246
            $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'];
247
            if (!empty($_SERVER['QUERY_STRING'])) {
248
                $_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
249
            }
250
            return $_SERVER['REQUEST_URI'];
251
        }
252
        return isset($_SERVER['ORIG_REQUEST_URI']) ? $_SERVER['ORIG_REQUEST_URI'] : $_SERVER['REQUEST_URI'];
253
    }
254
255
    /**
256
     * get the referer of the current request
257
     *
258
     * @return string
259
     */
260 1
    public function getReferer()
261
    {
262 1
        return $this->getEnv('HTTP_REFERER') ? $this->getEnv('HTTP_REFERER') : '';
263
    }
264
265
    /**
266
     * get the current script name associated with the request
267
     *
268
     * @return string
269
     */
270 1
    public function getScriptName()
271
    {
272 1
        return $this->getEnv('SCRIPT_NAME')
273 1
            ? $this->getEnv('SCRIPT_NAME')
274 1
            : ($this->getEnv('ORIG_SCRIPT_NAME') ? $this->getEnv('ORIG_SCRIPT_NAME') : '');
275
    }
276
277
    /**
278
     * Get the domain name and include $tldLength segments of the tld.
279
     *
280
     * @return string Domain name without subdomains
281
     */
282 1
    public function getDomain()
283
    {
284 1
        $host = $this->getHost();
285 1
        $domain =  \Xoops::getInstance()->getBaseDomain($host, false);
286 1
        return is_null($domain) ? $host : $domain;
287
    }
288
289
    /**
290
     * Get the subdomains for a host.
291
     *
292
     * @return string subdomain portion of host name
293
     */
294 1
    public function getSubdomains()
295
    {
296 1
        $host = $this->getHost();
297 1
        $pdp = \Xoops::getInstance()->getBaseDomain($host, true, true);
298 1
        $subdomain = $pdp->subdomain;
299 1
        return is_null($subdomain) ? '' : $subdomain;
300
    }
301
302
    /**
303
     * Get the Client IP address, optionally attempting to peek behind any proxies
304
     * to get a real routable address.
305
     *
306
     * @param boolean $considerProxy true to enable proxy tests
307
     *
308
     * @return string
309
     */
310
    public function getClientIp($considerProxy = false)
311
    {
312
        $default = (array_key_exists('REMOTE_ADDR', $_SERVER)) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
313
314
        if (!$considerProxy) {
315
            return $default;
316
        }
317
318
        $keys = array(
319
            'HTTP_CLIENT_IP',
320
            'HTTP_X_FORWARDED_FOR',
321
            'HTTP_X_FORWARDED',
322
            'HTTP_X_CLUSTER_CLIENT_IP',
323
            'HTTP_FORWARDED_FOR',
324
            'HTTP_FORWARDED',
325
        );
326
        foreach ($keys as $key) {
327
            if (array_key_exists($key, $_SERVER) === true) {
328
                foreach (explode(',', $_SERVER[$key]) as $ip) {
329
                    $ip = trim($ip);
330
                    if (false !== filter_var(
331
                        $ip,
332
                        FILTER_VALIDATE_IP,
333
                        FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
334
                    )) {
335
                        return $ip;
336
                    }
337
                }
338
            }
339
        }
340
341
        return $default;
342
    }
343
344
    /**
345
     * Return current url
346
     *
347
     * @return string
348
     */
349
    public function getUrl()
350
    {
351
        $url = $this->getScheme() . "://" . $this->getHost();
352
        $port = $this->getEnv('SERVER_PORT');
353
        if (80 != $port) {
354
            $url .= ":{$port}";
355
        }
356
        return $url . $this->getUri();
357
    }
358
359
    /**
360
     * Gets an environment variable from available sources, and provides emulation
361
     * for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on
362
     * IIS, or SCRIPT_NAME in CGI mode).  Also exposes some additional custom
363
     * environment information.
364
     * Note : code modifications for XOOPS
365
     *
366
     * @param string $name    Environment variable name.
367
     * @param mixed  $default default value
368
     *
369
     * @return string|boolean Environment variable setting.
370
     * @link http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#env
371
     *
372
     * @todo this methods and Xoops::getEnv() need to be unified
373
     */
374 14
    public function getEnv($name, $default = null)
375
    {
376 14
        if ($name === 'HTTPS') {
377 1
            if (isset($_SERVER['HTTPS'])) {
378 1
                return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
379
            }
380 1
            return (strpos($this->getEnv('SCRIPT_URI'), 'https://') === 0);
381
        }
382
383 14
        if ($name === 'SCRIPT_NAME' && !isset($_SERVER[$name])) {
384
            if ($this->getEnv('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) {
385
                return $_ENV['SCRIPT_URL'];
386
            }
387
        }
388
389 14
        if ($name === 'REMOTE_ADDR' && !isset($_SERVER[$name])) {
390
            $address = $this->getEnv('HTTP_PC_REMOTE_ADDR');
391
            if ($address !== null) {
392
                return $address;
393
            }
394
        }
395
396 14
        $val = null;
397 14
        if (isset($_SERVER[$name])) {
398 13
            $val = $_SERVER[$name];
399 7
        } elseif (isset($_ENV[$name])) {
400 1
            $val = $_ENV[$name];
401
        }
402
403 14
        if ($val !== null) {
404 13
            return $val;
405
        }
406
407
        switch ($name) {
408 7
            case 'SCRIPT_FILENAME':
409 1
                $val = preg_replace('#//+#', '/', $this->getEnv('PATH_TRANSLATED'));
410 1
                return preg_replace('#\\\\+#', '\\', $val);
411
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
412 6
            case 'DOCUMENT_ROOT':
413 1
                $name = $this->getEnv('SCRIPT_NAME');
414 1
                $filename = $this->getEnv('SCRIPT_FILENAME');
415 1
                $offset = 0;
416 1
                if (!strpos($name, '.php')) {
417 1
                    $offset = 4;
418
                }
419 1
                return substr($filename, 0, -(strlen($name) + $offset));
420
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
421 5
            case 'PHP_SELF':
422 1
                return str_replace($this->getEnv('DOCUMENT_ROOT'), '', $this->getEnv('SCRIPT_FILENAME'));
423
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
424 4
            case 'CGI_MODE':
425 1
                return (PHP_SAPI === 'cgi');
426
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
427 3
            case 'HTTP_BASE':
428 1
                $host = $this->getEnv('HTTP_HOST');
429 1
                $val = \Xoops::getInstance()->getBaseDomain($host);
430 1
                if (is_null($val)) {
431 1
                    return $default;
432
                } else {
433 1
                    return '.' . $val;
434
                }
435
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
436
        }
437 2
        return $default;
438
    }
439
440
    /**
441
     * get files associated with the current request
442
     *
443
     * @param string $name name of file
444
     *
445
     * @return array
446
     */
447
    public static function getFiles($name)
448
    {
449
        if (empty($_FILES)) {
450
            return array();
451
        }
452
453
        if (isset($_FILES[$name])) {
454
            return $_FILES[$name];
455
        }
456
457
        if (false === $pos = strpos($name, '[')) {
458
            return array();
459
        }
460
461
        $base = substr($name, 0, $pos);
462
        $key = str_replace(array(']', '['), array('', '"]["'), substr($name, $pos + 1, -1));
463
        $code = array(sprintf('if (!isset($_FILES["%s"]["name"]["%s"])) return array();', $base, $key));
464
        $code[] = '$file = array();';
465
        foreach (array('name', 'type', 'size', 'tmp_name', 'error') as $property) {
466
            $code[] = sprintf('$file["%1$s"] = $_FILES["%2$s"]["%1$s"]["%3$s"];', $property, $base, $key);
467
        }
468
        $code[] = 'return $file;';
469
470
        return eval(implode(PHP_EOL, $code));
471
    }
472
473
    /**
474
     * Check whether or not a Request is a certain type.  Uses the built in detection rules
475
     * as well as additional rules defined with HttpRequest::addDetector().  Any detector can be called
476
     * as `is($type)` or `is$Type()`.
477
     *
478
     * @param string $type The type of request you want to check.
479
     *
480
     * @return boolean Whether or not the request is the type you are checking.
481
     */
482
    public function is($type)
483
    {
484
        $type = strtolower($type);
485
        if (!isset($this->detectors[$type])) {
486
            return false;
487
        }
488
        $detect = $this->detectors[$type];
489
        if (isset($detect['env'])) {
490
            return $this->detectByEnv($detect);
491
        } elseif (isset($detect['param'])) {
492
            return $this->detectByParam($detect);
493
        } elseif (isset($detect['callback']) && is_callable($detect['callback'])) {
494
            return call_user_func($detect['callback'], $this);
495
        }
496
        return false;
497
    }
498
499
    /**
500
     * detectByEnv - perform detection on detectors with an 'env' component
501
     *
502
     * @param array $detect a detectors array entry to test against
503
     *
504
     * @return boolean true if detect is matched, false if not
505
     */
506
    protected function detectByEnv($detect)
507
    {
508
        if (isset($detect['value'])) {
509
            return (bool) $this->getEnv($detect['env']) == $detect['value'];
510
        } elseif (isset($detect['pattern'])) {
511
            return (bool) preg_match($detect['pattern'], $this->getEnv($detect['env']));
512
        } elseif (isset($detect['options'])) {
513
            $pattern = '/' . implode('|', $detect['options']) . '/i';
514
            return (bool) preg_match($pattern, $this->getEnv($detect['env']));
515
        }
516
        return false; // can't match a broken rule
517
    }
518
519
    /**
520
     * detectByParam - perform detection on detectors with an 'param' component.
521
     * To match an entry with the name in the 'param' key of the $detect rule must
522
     * exist in the $params property and be equal to the 'value' entry specified
523
     * in the $detect array.
524
     *
525
     * @param array $detect a detectors array entry to test against. Param entries are
526
     *                      of the form array('param' => name, 'value' => value)
527
     *
528
     * @return boolean true if detect is matched, false if not
529
     */
530
    protected function detectByParam($detect)
531
    {
532
        $name = $detect['param'];
533
        $value = $detect['value'];
534
        return isset($this->params[$name]) ? $this->params[$name] == $value : false;
535
    }
536
537
    /**
538
     * Add a new detector to the list of detectors that a request can use.
539
     * There are several different formats and types of detectors that can be set.
540
     * ### Environment value comparison
541
     * An environment value comparison, compares a value fetched from `env()` to a known value
542
     * the environment value is equality checked against the provided value.
543
     * e.g `addDetector('post', array('env' => 'REQUEST_METHOD', 'value' => 'POST'))`
544
     * ### Pattern value comparison
545
     * Pattern value comparison allows you to compare a value fetched from `env()` to a regular expression.
546
     * e.g `addDetector('iphone', array('env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i'));`
547
     * ### Option based comparison
548
     * Option based comparisons use a list of options to create a regular expression.  Subsequent calls
549
     * to add an already defined options detector will merge the options.
550
     * e.g `addDetector('mobile', array('env' => 'HTTP_USER_AGENT', 'options' => array('Fennec')));`
551
     * ### Callback detectors
552
     * Callback detectors allow you to provide a 'callback' type to handle the check.  The callback will
553
     * receive the request object as its only parameter.
554
     * e.g `addDetector('custom', array('callback' => array('SomeClass', 'someMethod')));`
555
     * ### Request parameter detectors
556
     * Allows for custom detectors on the request parameters.
557
     * e.g `addDetector('post', array('param' => 'requested', 'value' => 1)`
558
     *
559
     * @param string $name    The name of the detector.
560
     * @param array  $options The options for the detector definition.  See above.
561
     *
562
     * @return void
563
     */
564
    public function addDetector($name, $options)
565
    {
566
        $name = strtolower($name);
567
        if (isset($this->detectors[$name]) && isset($options['options'])) {
568
            $options = \Xoops\Utils::arrayRecursiveMerge($this->detectors[$name], $options);
569
        }
570
        $this->detectors[$name] = $options;
571
    }
572
573
    /**
574
     * Determine if a client accepts a given media type
575
     *
576
     * @param string $mediaType The content type to check for.
577
     *
578
     * @return boolean true if client accepts the media type, otherwise false
579
     */
580 1
    public function clientAcceptsType($mediaType)
581
    {
582 1
        $accepts = $this->getAcceptMediaTypes();
583
584 1
        $mediaType = trim($mediaType);
585 1
        if (isset($accepts[$mediaType])) {
586 1
            return true;
587
        }
588 1
        list($type) = explode('/', $mediaType);
589 1
        if (isset($accepts[$type.'/*'])) {
590 1
            return true;
591
        }
592
593 1
        return isset($accepts['*/*']);
594
    }
595
596
    /**
597
     * getAcceptMediaTypes returns the http-accept header as an
598
     * array of media types arranged by specified preference
599
     *
600
     * @return array associative array of preference (numeric weight >0 <=1.0 )
601
     *               keyed by media types, and sorted by preference
602
     */
603 1 View Code Duplication
    public function getAcceptMediaTypes()
604
    {
605 1
        $types = array();
606 1
        $accept = $this->getHeader('ACCEPT');
607
608 1
        if (!empty($accept)) {
609 1
            $entries = explode(',', $accept);
610 1
            foreach ($entries as $e) {
611 1
                $mt = explode(';q=', $e);
612 1
                if (!isset($mt[1])) {
613 1
                    $mt[1] = 1.0;
614
                }
615 1
                $types[trim($mt[0])] = (float) $mt[1];
616
            }
617
618
            // sort list based on value
619 1
            arsort($types, SORT_NUMERIC);
620
        }
621
622 1
        return($types);
623
    }
624
625
    /**
626
     * getAcceptedLanguages returns the http-accept-language header as an
627
     * array of language codes arranged by specified preference
628
     *
629
     * @return array associative array of preference (numeric weight >0 <=1.0 )
630
     *               keyed by language code, and sorted by preference
631
     */
632 1 View Code Duplication
    public function getAcceptedLanguages()
633
    {
634 1
        $languages = array();
635 1
        $accept = $this->getHeader('ACCEPT_LANGUAGE');
636
637 1
        if (!empty($accept)) {
638 1
            $entries = explode(',', $accept);
639 1
            foreach ($entries as $e) {
640 1
                $l = explode(';q=', $e);
641 1
                if (!isset($l[1])) {
642 1
                    $l[1] = 1.0;
643
                }
644 1
                $languages[trim($l[0])] = (float) $l[1];
645
            }
646
647
            // sort list based on value
648 1
            arsort($languages, SORT_NUMERIC);
649
        }
650
651 1
        return($languages);
652
    }
653
}
654