Completed
Branch 3.0.0 (3f0ab7)
by Serhii
03:15
created

Detector::analyse()   D

Complexity

Conditions 14
Paths 48

Size

Total Lines 82
Code Lines 50

Duplication

Lines 26
Ratio 31.71 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 14
eloc 50
c 3
b 0
f 0
nc 48
nop 2
dl 26
loc 82
rs 4.955

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
13
use Composer\DependencyResolver\Rule;
14
15
class Detector
16
{
17
    /**
18
     * @return string Path to directory with xml data files
19
     */
20
    public function getPathToData()
21
    {
22
        return $this->PathToData;
23
    }
24
25
    /**
26
     * @return array Xml data object
27
     */
28
    public function getXmlData()
29
    {
30
        return $this->xmlData;
31
    }
32
33
    /**
34
     * @param string $PathToData
35
     */
36
    public function setPathToData($PathToData)
37
    {
38
        $this->PathToData = $PathToData;
39
    }
40
41
    /**
42
     * @param array $xmlData Xml data object
43
     */
44
    public function setXmlData($xmlData)
45
    {
46
        $this->xmlData = $xmlData;
47
    }
48
    /** @var  string Path to directory with xml data */
49
    private $PathToData;
50
51
    /** @var array Xml Data */
52
    private $xmlData;
53
54
    private $Rules;
55
56
    /**
57
     * Detector constructor.
58
     * @param string $pathToData Path to directory with xml data files
59
     */
60
    private function __construct($pathToData='auto')
61
    {
62
        if($pathToData == 'auto')
63
        {
64
            $this->setPathToData(str_replace('src','data',__DIR__).'/');
65
        }
66
67
        $xml = array('robot','browser','device','os');
68
        $xmlData = array();
69
        foreach($xml as $name)
70
        {
71
            $xmlData[$name] = simplexml_load_file($this->getPathToData().$name.'.xml');
72
        }
73
        $this->setXmlData($xmlData);
74
75
        $this->Rules = DetectorRule::loadRulesFromFile();
76
    }
77
78
    public static function analyse($uaString='UA', $pathToData='auto')
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...
79
    {
80
        $ua = $uaString;
81
        if($uaString == 'UA')
82
        {
83
            $ua = $_SERVER['HTTP_USER_AGENT'];
84
        }
85
86
        $detector = new Detector($pathToData);
87
        $xml = $detector->getXmlData();
88
        $data = array();
89
        foreach($xml as $key => $item)
90
        {
91
            $data[$key] = self::analysePart($xml,$key,$ua);
92
        }
93
94
        $detectorResult = new DetectorResult();
95
        $detectorResult->uaString = $ua;
96
        $isRobot = false;
97
98
        foreach($data as $key => $result)
99
        {
100
            if(!$isRobot)
101
            {
102
                switch ($key)
103
                {
104
                    case 'robot':
105
                        if($result !== null)
106
                        {
107
                            $robot = new Robot($result);
108
                            $detectorResult->Robot = $robot;
109
                            if ($robot->getName() != 'N\A') {
110
                                $isRobot = true;
111
                            }
112
                        }
113
                        break;
114 View Code Duplication
                    case 'os':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
115
                        if($result !== null)
116
                        {
117
                            $os = new OS($result);
118
                            $os->setVersion(self::getVersion($result, $ua));
119
                            $detectorResult->OS = $os;
120
                        }
121
                        else
122
                        {
123
                            $os = OS::initEmpty();
124
                            $detectorResult->OS = $os;
125
                        }
126
                        break;
127
                    case 'device':
128
                        if($result !== null)
129
                        {
130
                            $device = new Device($result);
131
                            $detectorResult->Device = $device;
132
                        }
133
                        else
134
                        {
135
                            $device = Device::initEmpty();
136
                            $detectorResult->Device = $device;
137
                        }
138
                        break;
139 View Code Duplication
                    case 'browser':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
                        if($result !== null)
141
                        {
142
                            $browser = new Browser($result);
143
                            $browser->setVersion(self::getVersion($result, $ua));
144
                            $detectorResult->Browser = $browser;
145
                        }
146
                        else
147
                        {
148
                            $browser = Browser::initEmpty();
149
                            $detectorResult->Browser = $browser;
150
                        }
151
                        break;
152
                }
153
            }
154
        }
155
156
        $detectorResult = $detector->ckeckRules($detectorResult);
157
158
        return $detectorResult;
159
    }
160
161
    /**
162
     * @param array $xmlData Xml data array
163
     * @param string $key Key in data array
164
     * @param string $uaString User agent
165
     * @return \SimpleXMLElement xml element
166
     */
167
    private static function analysePart($xmlData,$key,$uaString)
168
    {
169
        $data = $xmlData[$key]->data;
170
        foreach($data as $xmlItem)
171
        {
172
            $pattern = '/'.$xmlItem->pattern.'/';
173
            if(preg_match($pattern,$uaString))
174
            {
175
                return $xmlItem;
176
            }
177
        }
178
        return null;
179
    }
180
181
    /**
182
     * @param \SimpleXMLElement $xmlItem xmlItem
183
     * @param string $uaString User agent
184
     * @return string Version
185
     */
186
    private static function getVersion(\SimpleXMLElement $xmlItem,$uaString)
187
    {
188
        if($xmlItem !== null)
189
        {
190
            foreach($xmlItem->children() as $node)
191
            {
192
                if($node->getName() == 'versionPattern')
193
                {
194
                    $vPattern = $node->__toString();
195
                    $version = '/' . $vPattern . '(\/| )[\w-._]{1,15}/';
196
                    $uaString = str_replace(' NT', '', $uaString);
197
                    if (preg_match($version, $uaString)) {
198
                        preg_match($version, $uaString, $v);
199
                        $version = $v[0];
200
                        $version = preg_replace('/' . $vPattern . '/', '', $version);
201
                        $version = str_replace(';', '', $version);
202
                        $version = str_replace(' ', '', $version);
203
                        $version = str_replace('/', '', $version);
204
                        $version = str_replace('_', '.', $version);
205
206
                        if ($xmlItem->id == 'Windows') {
207
                            $version = self::getWindowsVersion($version);
208
                        }
209
210
                        return $version;
211
                    }
212
                }
213
            }
214
        }
215
        return D_NA;
216
    }
217
218
    /**
219
     * @param $version Windows number version
220
     * @return string Windows version
221
     */
222
    private static function getWindowsVersion($version)
223
    {
224
        $versions = array(
225
            '5.1' => 'XP',
226
            '5.2' => 'Server 2003',
227
            '6.0' => 'Vista',
228
            '6.1' => '7',
229
            '6.2' => '8',
230
            '6.3' => '8.1',
231
            '6.4' => '10'
232
        );
233
234
        return $versions[$version];
235
    }
236
237
    /**
238
     * @param DetectorResult $DetectorResult Detector result
0 ignored issues
show
Documentation introduced by
There is no parameter named $DetectorResult. Did you maybe mean $result?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
239
     * @return DetectorResult Final result
240
     */
241
    private function ckeckRules(\EndorphinStudio\Detector\DetectorResult $result)
242
    {
243
        foreach($this->Rules as $rule)
244
        {
245
            $objectType = $rule->getObjectType();
246
            $objectProperty = $rule->getObjectProperty();
247
            $targetType = $rule->getTargetType();
248
            $targetValue = $rule->isTargetValue();
249
            $func = 'get'.$objectProperty;
250
            if($result->$objectType !== null)
251
            {
252
                if ($result->$objectType->$func() == $rule->getObjectPropertyValue()) {
253
                    $result->$targetType = $targetValue;
254
                    break;
255
                }
256
            }
257
        }
258
        return $result;
259
    }
260
}
261
define('D_NA','N\A');