1
|
|
|
<?php |
2
|
|
|
namespace SEOstats\Services; |
3
|
|
|
|
4
|
|
|
/** |
5
|
|
|
* SEOstats extension for Alexa data. |
6
|
|
|
* |
7
|
|
|
* @package SEOstats |
8
|
|
|
* @author Stephan Schmitz <[email protected]> |
9
|
|
|
* @copyright Copyright (c) 2010 - present Stephan Schmitz |
10
|
|
|
* @license http://eyecatchup.mit-license.org/ MIT License |
11
|
|
|
* @updated 2013/08/17 |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
use SEOstats\SEOstats as SEOstats; |
15
|
|
|
use SEOstats\Config as Config; |
16
|
|
|
use SEOstats\Helper as Helper; |
17
|
|
|
|
18
|
|
|
class Alexa extends SEOstats |
19
|
|
|
{ |
20
|
|
|
/** |
21
|
|
|
* Used for cache |
22
|
|
|
* @var DOMXPath |
23
|
|
|
*/ |
24
|
|
|
protected static $_xpath = false; |
25
|
|
|
|
26
|
|
|
protected static $_rankKeys = array( |
27
|
|
|
'1d' => 0, |
28
|
|
|
'7d' => 0, |
29
|
|
|
'1m' => 0, |
30
|
|
|
'3m' => 0, |
31
|
|
|
); |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Get yesterday's rank |
35
|
|
|
* @return int |
36
|
|
|
*/ |
37
|
3 |
View Code Duplication |
public static function getDailyRank($url = false) |
|
|
|
|
38
|
|
|
{ |
39
|
3 |
|
self::setRankingKeys($url); |
40
|
3 |
|
if (0 == self::$_rankKeys['1d']) { |
41
|
1 |
|
return parent::noDataDefaultValue(); |
|
|
|
|
42
|
|
|
} |
43
|
|
|
|
44
|
2 |
|
$xpath = self::_getXPath($url); |
45
|
2 |
|
$nodes = @$xpath->query("//*[@id='rank']/table/tr[" . self::$_rankKeys['1d'] . "]/td[1]"); |
46
|
|
|
|
47
|
2 |
|
return !$nodes->item(0) ? parent::noDataDefaultValue() : |
|
|
|
|
48
|
2 |
|
self::retInt( strip_tags($nodes->item(0)->nodeValue) ); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* For backward compatibility |
53
|
|
|
* @deprecated |
54
|
|
|
*/ |
55
|
1 |
|
public static function getWeekRank($url = false) { |
56
|
1 |
|
return self::getWeeklyRank($url); |
57
|
|
|
} |
58
|
|
|
/** |
59
|
|
|
* Get the average rank over the last 7 days |
60
|
|
|
* @return int |
61
|
|
|
*/ |
62
|
4 |
View Code Duplication |
public static function getWeeklyRank($url = false) |
|
|
|
|
63
|
|
|
{ |
64
|
4 |
|
self::setRankingKeys($url); |
65
|
4 |
|
if (0 == self::$_rankKeys['7d']) { |
66
|
1 |
|
return parent::noDataDefaultValue(); |
|
|
|
|
67
|
|
|
} |
68
|
|
|
|
69
|
3 |
|
$xpath = self::_getXPath($url); |
70
|
3 |
|
$nodes = @$xpath->query("//*[@id='rank']/table/tr[" . self::$_rankKeys['7d'] . "]/td[1]"); |
71
|
|
|
|
72
|
3 |
|
return !$nodes->item(0) ? parent::noDataDefaultValue() : |
|
|
|
|
73
|
3 |
|
self::retInt( strip_tags($nodes->item(0)->nodeValue) ); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* For backward compatibility |
78
|
|
|
* @deprecated |
79
|
|
|
*/ |
80
|
1 |
|
public static function getMonthRank($url = false) { |
81
|
1 |
|
return self::getMonthlyRank($url); |
82
|
|
|
} |
83
|
|
|
/** |
84
|
|
|
* Get the average rank over the last month |
85
|
|
|
* @return int |
86
|
|
|
*/ |
87
|
4 |
View Code Duplication |
public static function getMonthlyRank($url = false) |
|
|
|
|
88
|
|
|
{ |
89
|
4 |
|
self::setRankingKeys($url); |
90
|
4 |
|
if (0 == self::$_rankKeys['1m']) { |
91
|
1 |
|
return parent::noDataDefaultValue(); |
|
|
|
|
92
|
|
|
} |
93
|
|
|
|
94
|
3 |
|
$xpath = self::_getXPath($url); |
95
|
3 |
|
$nodes = @$xpath->query("//*[@id='rank']/table/tr[" . self::$_rankKeys['1m'] . "]/td[1]"); |
96
|
|
|
|
97
|
3 |
|
return !$nodes->item(0) ? parent::noDataDefaultValue() : |
|
|
|
|
98
|
3 |
|
self::retInt( strip_tags($nodes->item(0)->nodeValue) ); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* For backward compatibility |
103
|
|
|
* @deprecated |
104
|
|
|
*/ |
105
|
2 |
|
public static function getQuarterRank($url = false) { |
106
|
2 |
|
return self::getGlobalRank($url); |
107
|
|
|
} |
108
|
|
|
/** |
109
|
|
|
* Get the average rank over the last 3 months |
110
|
|
|
* @return int |
111
|
|
|
*/ |
112
|
4 |
|
public static function getGlobalRank($url = false) |
113
|
|
|
{ |
114
|
|
|
/* |
115
|
|
|
self::setRankingKeys($url); |
116
|
|
|
if (0 == self::$_rankKeys['3m']) { |
117
|
|
|
return parent::noDataDefaultValue(); |
118
|
|
|
} |
119
|
|
|
*/ |
120
|
|
|
|
121
|
4 |
|
$xpath = self::_getXPath($url); |
122
|
|
|
|
123
|
|
|
$xpathQueryList = array( |
124
|
4 |
|
"//*[@id='traffic-rank-content']/div/span[2]/div[1]/span/span/div/strong", |
125
|
|
|
"//*[@id='traffic-rank-content']/div/span[2]/div[1]/span/span/div/strong/a" |
126
|
4 |
|
); |
127
|
|
|
|
128
|
4 |
|
return static::parseDomByXpathsToIntegerWithoutTags($xpath, $xpathQueryList); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Get the average rank over the week |
133
|
|
|
* @return int |
134
|
|
|
*/ |
135
|
11 |
|
public static function setRankingKeys($url = false) |
136
|
|
|
{ |
137
|
11 |
|
$xpath = self::_getXPath($url); |
138
|
11 |
|
$nodes = @$xpath->query("//*[@id='rank']/table/tr"); |
139
|
|
|
|
140
|
11 |
|
if (5 == $nodes->length) { |
141
|
6 |
|
self::$_rankKeys = array( |
142
|
6 |
|
'1d' => 2, |
143
|
6 |
|
'7d' => 3, |
144
|
6 |
|
'1m' => 4, |
145
|
6 |
|
'3m' => 5, |
146
|
6 |
|
); |
147
|
6 |
|
} |
148
|
5 |
|
else if (4 == $nodes->length) { |
149
|
1 |
|
self::$_rankKeys = array( |
150
|
1 |
|
'1d' => 0, |
151
|
1 |
|
'7d' => 2, |
152
|
1 |
|
'1m' => 3, |
153
|
1 |
|
'3m' => 4, |
154
|
1 |
|
); |
155
|
1 |
|
} |
156
|
4 |
|
else if (3 == $nodes->length) { |
157
|
1 |
|
self::$_rankKeys = array( |
158
|
1 |
|
'1d' => 0, |
159
|
1 |
|
'7d' => 0, |
160
|
1 |
|
'1m' => 2, |
161
|
1 |
|
'3m' => 3, |
162
|
1 |
|
); |
163
|
1 |
|
} |
164
|
3 |
|
else if (2 == $nodes->length) { |
165
|
3 |
|
self::$_rankKeys = array( |
166
|
3 |
|
'1d' => 0, |
167
|
3 |
|
'7d' => 0, |
168
|
3 |
|
'1m' => 0, |
169
|
3 |
|
'3m' => 2, |
170
|
3 |
|
); |
171
|
3 |
|
} |
172
|
11 |
|
} |
173
|
|
|
|
174
|
3 |
|
public static function getCountryRank($url = false) |
175
|
|
|
{ |
176
|
3 |
|
$xpath = self::_getXPath($url); |
177
|
3 |
|
$node1 = self::parseDomByXpaths($xpath, array( |
178
|
3 |
|
"//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/h4/a", |
179
|
3 |
|
"//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/h4/strong/a", |
180
|
3 |
|
)); |
181
|
|
|
|
182
|
3 |
|
$node2 = self::parseDomByXpaths($xpath, array( |
183
|
3 |
|
"//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/div/strong/a", |
184
|
3 |
|
"//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/div/strong", |
185
|
3 |
|
)); |
186
|
|
|
|
187
|
3 |
|
$node3 = self::parseDomByXpaths($xpath, array( |
188
|
3 |
|
"//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/h4/a/@href", |
189
|
3 |
|
"//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/h4/strong/a/@href", |
190
|
3 |
|
)); |
191
|
|
|
|
192
|
3 |
|
if (!is_null($node2) && $node2->item(0)) { |
193
|
3 |
|
$rank = self::retInt(strip_tags($node2->item(0)->nodeValue)); |
194
|
3 |
|
$country_code = str_replace("/topsites/countries/", "", $node3->item(0)->nodeValue); |
195
|
3 |
|
if ($node1->item(0) && 0 != $rank) { |
196
|
|
|
return array( |
197
|
3 |
|
'rank' => $rank, |
198
|
3 |
|
'country' => $node1->item(0)->nodeValue, |
199
|
3 |
|
'country_code' => $country_code, |
200
|
3 |
|
); |
201
|
|
|
} |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
return parent::noDataDefaultValue(); |
|
|
|
|
205
|
|
|
} |
206
|
|
|
|
207
|
1 |
View Code Duplication |
public static function getBacklinkCount($url = false) |
|
|
|
|
208
|
|
|
{ |
209
|
1 |
|
$xpath = self::_getXPath($url); |
210
|
|
|
|
211
|
|
|
$queryList = array( |
212
|
1 |
|
"//section[@id='linksin-panel-content']/div/span/div/span", |
213
|
|
|
"//*[@id='linksin_div']/section/div/div[1]/span" |
214
|
1 |
|
); |
215
|
|
|
|
216
|
1 |
|
return static::parseDomByXpathsToInteger($xpath, $queryList); |
217
|
|
|
} |
218
|
|
|
|
219
|
2 |
View Code Duplication |
public static function getPageLoadTime($url = false) |
|
|
|
|
220
|
|
|
{ |
221
|
2 |
|
$xpath = self::_getXPath($url); |
222
|
|
|
|
223
|
|
|
$queryList = array( |
224
|
2 |
|
"//section[@class='row-fluid panel-wrapper '][9]/section/p", |
225
|
|
|
"//*[@id='section-load']/div/section/p" |
226
|
2 |
|
); |
227
|
|
|
|
228
|
2 |
|
return static::parseDomByXpathsWithoutTags($xpath, $queryList); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* @access public |
233
|
|
|
* @param integer $type Specifies the graph type. Valid values are 1 to 6. |
234
|
|
|
* @param integer $width Specifies the graph width (in px). |
|
|
|
|
235
|
|
|
* @param integer $height Specifies the graph height (in px). |
|
|
|
|
236
|
|
|
* @param integer $period Specifies the displayed time period. Valid values are 1 to 12. |
237
|
|
|
* @return string Returns a string, containing the HTML code of an image, showing Alexa Statistics as Graph. |
238
|
|
|
*/ |
239
|
13 |
|
public static function getTrafficGraph($type = 1, $url = false, $w = 660, $h = 330, $period = 1, $html = true) |
240
|
|
|
{ |
241
|
13 |
|
$url = self::getUrl($url); |
242
|
13 |
|
$domain = Helper\Url::parseHost($url); |
243
|
|
|
|
244
|
|
|
switch($type) { |
245
|
13 |
|
case 1: $gtype = 't'; break; |
246
|
7 |
|
case 2: $gtype = 'p'; break; |
247
|
6 |
|
case 3: $gtype = 'u'; break; |
248
|
5 |
|
case 4: $gtype = 's'; break; |
249
|
4 |
|
case 5: $gtype = 'b'; break; |
250
|
3 |
|
case 6: $gtype = 'q'; break; |
251
|
2 |
|
default: break; |
252
|
2 |
|
} |
253
|
|
|
|
254
|
13 |
|
$imgUrl = sprintf(Config\Services::ALEXA_GRAPH_URL, $gtype, $w, $h, $period, $domain); |
|
|
|
|
255
|
11 |
|
$imgTag = '<img src="%s" width="%s" height="%s" alt="Alexa Statistics Graph for %s"/>'; |
256
|
|
|
|
257
|
11 |
|
return !$html ? $imgUrl : sprintf($imgTag, $imgUrl, $w, $h, $domain); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* @return DOMXPath |
262
|
|
|
*/ |
263
|
22 |
|
protected static function _getXPath($url) { |
264
|
22 |
|
$url = parent::getUrl($url); |
|
|
|
|
265
|
22 |
|
if (parent::getLastLoadedUrl() == $url && self::$_xpath) { |
|
|
|
|
266
|
1 |
|
return self::$_xpath; |
267
|
|
|
} |
268
|
|
|
|
269
|
22 |
|
$html = static::_getAlexaPage($url); |
270
|
22 |
|
$doc = parent::_getDOMDocument($html); |
|
|
|
|
271
|
22 |
|
$xpath = parent::_getDOMXPath($doc); |
|
|
|
|
272
|
|
|
|
273
|
22 |
|
self::$_xpath = $xpath; |
|
|
|
|
274
|
|
|
|
275
|
22 |
|
return $xpath; |
|
|
|
|
276
|
|
|
} |
277
|
|
|
|
278
|
2 |
|
protected static function _getAlexaPage($url) |
279
|
|
|
{ |
280
|
2 |
|
$domain = Helper\Url::parseHost($url); |
281
|
2 |
|
$dataUrl = sprintf(Config\Services::ALEXA_SITEINFO_URL, $domain); |
282
|
2 |
|
$html = static::_getPage($dataUrl); |
283
|
2 |
|
return $html; |
284
|
|
|
} |
285
|
|
|
|
286
|
24 |
|
protected static function retInt($str) |
287
|
|
|
{ |
288
|
24 |
|
$strim = trim(str_replace(',', '', $str)); |
289
|
24 |
|
$intStr = 0 < strlen($strim) ? $strim : '0'; |
290
|
24 |
|
return intval($intStr); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* |
295
|
|
|
* @return mixed nodeValue |
296
|
|
|
*/ |
297
|
10 |
|
protected static function parseDomByXpaths($xpathDom, $xpathQueryList) { |
298
|
|
|
|
299
|
10 |
|
foreach ( $xpathQueryList as $query ) { |
300
|
10 |
|
$nodes = @$xpathDom->query($query); |
301
|
|
|
|
302
|
10 |
|
if ( $nodes->length != 0 ) { |
303
|
10 |
|
return $nodes; |
304
|
|
|
} |
305
|
5 |
|
} |
306
|
|
|
|
307
|
|
|
return null; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* |
312
|
|
|
* @return mixed nodeValue |
313
|
|
|
*/ |
314
|
7 |
|
protected static function parseDomByXpathsGetValue($xpathDom, $xpathQueryList) |
315
|
|
|
{ |
316
|
7 |
|
$nodes = static::parseDomByXpaths($xpathDom, $xpathQueryList); |
317
|
|
|
|
318
|
7 |
|
return ($nodes) ? $nodes->item(0)->nodeValue : null; |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
/** |
322
|
|
|
* |
323
|
|
|
* @return mixed nodeValue |
324
|
|
|
*/ |
325
|
1 |
View Code Duplication |
protected static function parseDomByXpathsToInteger($xpathDom, $xpathQueryList) |
|
|
|
|
326
|
|
|
{ |
327
|
1 |
|
$nodeValue = static::parseDomByXpathsGetValue($xpathDom, $xpathQueryList); |
328
|
|
|
|
329
|
1 |
|
if ($nodeValue === null) { |
330
|
|
|
return parent::noDataDefaultValue(); |
|
|
|
|
331
|
|
|
} |
332
|
1 |
|
return self::retInt( $nodeValue ); |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* |
337
|
|
|
* @return mixed nodeValue |
338
|
|
|
*/ |
339
|
2 |
View Code Duplication |
protected static function parseDomByXpathsWithoutTags($xpathDom, $xpathQueryList) |
|
|
|
|
340
|
|
|
{ |
341
|
|
|
|
342
|
2 |
|
$nodeValue = static::parseDomByXpathsGetValue($xpathDom, $xpathQueryList); |
343
|
|
|
|
344
|
2 |
|
if ($nodeValue === null) { |
345
|
|
|
return parent::noDataDefaultValue(); |
|
|
|
|
346
|
|
|
} |
347
|
|
|
|
348
|
2 |
|
return strip_tags($nodeValue); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* |
353
|
|
|
* @return mixed nodeValue |
354
|
|
|
*/ |
355
|
4 |
View Code Duplication |
protected static function parseDomByXpathsToIntegerWithoutTags($xpathDom, $xpathQueryList) |
|
|
|
|
356
|
|
|
{ |
357
|
4 |
|
$nodeValue = static::parseDomByXpathsGetValue($xpathDom, $xpathQueryList); |
358
|
|
|
|
359
|
4 |
|
if ($nodeValue === null) { |
360
|
|
|
return parent::noDataDefaultValue(); |
|
|
|
|
361
|
|
|
} |
362
|
|
|
|
363
|
4 |
|
return self::retInt(strip_tags($nodeValue)); |
364
|
|
|
} |
365
|
|
|
} |
366
|
|
|
|
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.