Completed
Pull Request — master (#8)
by Serhii
01:59
created

Detector::analyse()   C

Complexity

Conditions 9
Paths 32

Size

Total Lines 47
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
c 2
b 1
f 1
dl 0
loc 47
rs 5.2941
cc 9
eloc 26
nc 32
nop 1
1
<?php
2
/**
3
 * @author Sergey Nehaenko <[email protected]>
4
 * @license GPL
5
 * @copyright Sergey Nehaenko &copy 2016
6
 * @version 3.0.0
7
 * @project browser-detector
8
 */
9
10
namespace EndorphinStudio\Detector;
11
12
class Detector
13
{
14
    /** @var array Xml Data */
15
    private static $_xmlData;
16
    public static $isInitialized = false;
17
18
    private static function initialize($pathToData = 'auto')
19
    {
20
        if ($pathToData == 'auto')
21
        {
22
            $pathToData = str_replace('src', 'data', __DIR__) . '/';
23
        }
24
25
        if (self::$_xmlData === null)
26
        {
27
            $xml = array('Robot', 'Browser', 'Device', 'OS');
28
            $xmlData = array();
29
            foreach ($xml as $name) {
30
                $xmlData[$name] = simplexml_load_file($pathToData . strtolower($name) . '.xml');
31
            }
32
            self::$_xmlData = $xmlData;
33
            self::$isInitialized = true;
34
        }
35
    }
36
37
    public static function analyse($uaString='UA')
0 ignored issues
show
Coding Style introduced by
analyse uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
38
    {
39
        $ua = $uaString;
40
        if($uaString == 'UA')
41
        {
42
            $ua = $_SERVER['HTTP_USER_AGENT'];
43
        }
44
45
        if(!self::$isInitialized)
46
            self::initialize();
47
        $xml = self::$_xmlData;
48
49
        $detectorResult = new DetectorResult();
50
        $detectorResult->uaString = $ua;
51
        $ns = '\\EndorphinStudio\\Detector\\';
52
53
        foreach($xml as $key => $item)
54
        {
55
            $data = self::analysePart($xml,$key,$ua);
56
            $classname = $ns.$key;
57
            if($data !== null)
58
            {
59
                $object = new $classname($data);
60
                if($key == 'OS' || $key == 'Browser')
61
                {
62
                    $object->setVersion(self::getVersion($data, $ua));
63
                }
64
                if($key == 'Robot')
65
                {
66
                    if($object->getName() != D_NA)
67
                    {
68
                        $detectorResult->isBot = true;
69
                    }
70
                }
71
            }
72
            else
73
            {
74
                $object = $classname::initEmpty();
75
            }
76
            $detectorResult->$key = $object;
77
        }
78
79
        $detectorResult = self::checkRules($detectorResult);
80
        $detectorResult = self::checkModelName($detectorResult);
81
82
        return $detectorResult;
83
    }
84
85
    /**
86
     * @param array $xmlData Xml data array
87
     * @param string $key Key in data array
88
     * @param string $uaString User agent
89
     * @return \SimpleXMLElement xml element
90
     */
91
    private static function analysePart($xmlData,$key,$uaString)
92
    {
93
        $data = $xmlData[$key]->data;
94
        foreach($data as $xmlItem)
95
        {
96
            $pattern = '/'.$xmlItem->pattern.'/';
97
            if(preg_match($pattern,$uaString))
98
            {
99
                return $xmlItem;
100
            }
101
        }
102
        return null;
103
    }
104
105
    /**
106
     * @param \SimpleXMLElement $xmlItem xmlItem
107
     * @param string $uaString User agent
108
     * @return string Version
109
     */
110
    private static function getVersion(\SimpleXMLElement $xmlItem,$uaString)
111
    {
112
        if($xmlItem !== null)
113
        {
114
            foreach($xmlItem->children() as $node)
115
            {
116
                if($node->getName() == 'versionPattern')
117
                {
118
                    $vPattern = $node->__toString();
119
                    $version = '/' . $vPattern . '(\/| )[\w-._]{1,15}/';
120
                    $uaString = str_replace(' NT', '', $uaString);
121
                    if (preg_match($version, $uaString)) {
122
                        preg_match($version, $uaString, $v);
123
                        $version = $v[0];
124
                        $version = preg_replace('/' . $vPattern . '/', '', $version);
125
                        $version = str_replace(';', '', $version);
126
                        $version = str_replace(' ', '', $version);
127
                        $version = str_replace('/', '', $version);
128
                        $version = str_replace('_', '.', $version);
129
130
                        if ($xmlItem->id == 'Windows') {
131
                            $version = self::getWindowsVersion($version);
132
                        }
133
134
                        return $version;
135
                    }
136
                }
137
            }
138
        }
139
        return D_NA;
140
    }
141
142
    /**
143
     * @param $version Windows number version
144
     * @return string Windows version
145
     */
146
    private static function getWindowsVersion($version)
147
    {
148
        $versions = array(
149
            '95' => '95',
150
            '3.1' => '3.1',
151
            '3.5' => '3.5',
152
            '3.51' => '3.51',
153
            '4.0' => '4.0',
154
            '2000' => '2000',
155
            '5.0' => '2000',
156
            '5.1' => 'XP',
157
            '5.2' => 'Server 2003',
158
            '6.0' => 'Vista',
159
            '6.1' => '7',
160
            '6.2' => '8',
161
            '6.3' => '8.1',
162
            '6.4' => '10',
163
            '10.0' => '10'
164
        );
165
166
        if(array_key_exists(strval($version),$versions))
167
            return $versions[strval($version)];
168
        else
169
            return D_NA;
170
    }
171
172
    /**
173
     * @param DetectorResult $result Detector result
174
     * @return DetectorResult Final result
175
     */
176
    private static function checkRules(DetectorResult $result)
177
    {
178
        $Rules = DetectorRule::loadRulesFromFile();
179
        foreach($Rules as $rule)
180
        {
181
            $objectType = $rule->getObjectType();
182
            $objectProperty = $rule->getObjectProperty();
183
            $targetType = $rule->getTargetType();
184
            $targetValue = $rule->isTargetValue();
185
            $func = 'get'.$objectProperty;
186
            if($result->$objectType !== null)
187
            {
188
                if ($result->$objectType->$func() == $rule->getObjectPropertyValue()) {
189
                    $result->$targetType = $targetValue;
190
                    break;
191
                }
192
            }
193
        }
194
        return $result;
195
    }
196
197
    private static function checkModelName(DetectorResult $result)
198
    {
199
        $models = DeviceModel::loadFromFile();
200
        foreach($models as $model)
201
        {
202
            if($model->getDeviceName() === $result->Device->getName())
203
            {
204
                $pattern = '/'.$model->getPattern().'/';
205
                preg_match($pattern,$result->uaString,$match);
206
                $result->Device->setModelName($match[1]);
207
            }
208
        }
209
        return $result;
210
    }
211
}
212
define('D_NA','N\A');
213