Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 45
Browscap
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
342
0.00% covered (danger)
0.00%
0 / 45
 __construct
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 4
 getBrowser
0.00% covered (danger)
0.00%
0 / 1
20
0.00% covered (danger)
0.00%
0 / 9
 getDatabase
0.00% covered (danger)
0.00%
0 / 1
56
0.00% covered (danger)
0.00%
0 / 26
 quoteVar
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 6
1<?php
2
3namespace Miniframe\Statistics\Service;
4
5class Browscap
6{
7    /**
8     * Download URL for the database file
9     *
10     * @var string
11     */
12    protected $downloadUrl = 'https://browscap.org/stream?q=Lite_PHP_BrowsCapINI';
13
14    /**
15     * Filename for the .ini database file
16     *
17     * @var string
18     */
19    protected $iniPath;
20    /**
21     * Filename for the .php database file
22     *
23     * @var string
24     */
25    protected $phpPath;
26
27    /**
28     * Initiates the Browscap class
29     *
30     * @param string $browscapFolder The folder in which the database files can be stored.
31     */
32    public function __construct(string $browscapFolder)
33    {
34        if (!is_dir($browscapFolder)) {
35            mkdir($browscapFolder, 0777, true);
36        }
37        $this->iniPath = rtrim($browscapFolder, '/') . '/browscap.ini';
38        $this->phpPath = rtrim($browscapFolder, '/') . '/browscap.php';
39    }
40
41    /**
42     * Returns information about a browser
43     *
44     * @param string $userAgent The user agent.
45     *
46     * @return array
47     */
48    public function getBrowser(string $userAgent): array
49    {
50        // First try native PHP way
51        if (ini_get('browscap')) {
52            $result = get_browser($userAgent, true);
53            if ($result) {
54                return $result;
55            }
56        }
57
58        // Resolve the user agent
59        require $this->phpPath; /* @var $result array */
60
61        // If a parent is set, resolve the parent
62        if (isset($result['Parent'])) {
63            $result = array_merge($this->getBrowser($result['Parent']), $result);
64            unset($result['Parent']);
65        }
66
67        return $result;
68    }
69
70    /**
71     * Updates and interprets the database
72     *
73     * @return void
74     */
75    public function getDatabase(): void
76    {
77        // Download file
78        $fh = fopen($this->iniPath, 'wb');
79        $ch = curl_init();
80        curl_setopt_array($ch, [
81            CURLOPT_URL => $this->downloadUrl,
82            CURLOPT_HTTPGET => true,
83            CURLOPT_HEADER => false,
84            CURLOPT_FILE => $fh,
85        ]);
86        curl_exec($ch);
87        curl_close($ch);
88        fclose($fh);
89
90        // Convert ini file to simple PHP file
91        $ini = fopen($this->iniPath, 'r');
92        $php = fopen($this->phpPath, 'w');
93        fputs($php, '<?php' . PHP_EOL);
94        fputs($php, '// File generated by ' . __FILE__ . PHP_EOL);
95        fputs($php, '// Do NOT modify this manually!' . PHP_EOL);
96        fputs($php, '$result = array();' . PHP_EOL);
97        $first = true;
98        while ($row = fgets($ini, 1024)) {
99            if (substr($row, 0, 1) == '[') {
100                if (!$first) {
101                    fputs($php, '} else');
102                }
103                $first = false;
104                $regex = '/^'
105                    . str_replace(['\\*', '\\?'], ['.*', '.'], preg_quote(substr(trim($row), 1, -1), '/'))
106                    . '$/i';
107                fputs($php, 'if (preg_match(' . var_export($regex, true) . ', $userAgent)) {' . PHP_EOL);
108            } elseif ($row && substr($row, 0, 1) != ';' && strpos($row, '=') !== false) {
109                $parts = explode('=', $row, 2);
110                fputs(
111                    $php,
112                    "\t\$result[" . static::quoteVar($parts[0]) . "] = "
113                    . static::quoteVar($parts[1]) . ';' . PHP_EOL
114                );
115            }
116        }
117        fputs($php, '}' . PHP_EOL);
118        fclose($php);
119        fclose($ini);
120    }
121
122    /**
123     * Checks if a variable contains quotes, if not, add those
124     *
125     * @param string $var The variable.
126     *
127     * @return string
128     */
129    protected static function quoteVar(string $var): string
130    {
131        $var = trim($var);
132        if (substr($var, 0, 1) == '"' && substr($var, -1) == '"') {
133            return $var;
134        } elseif (substr($var, 0, 1) == "'" && substr($var, -1) == "'") {
135            return $var;
136        }
137        return var_export($var, true);
138    }
139}