diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 00000000..d2a0ad91 --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,14 @@ +imports: + - php + +tools: + external_code_coverage: + timeout: 2100 + +filter: + excluded_paths: + - SEOstats/Services/3rdparty/* + - vendor/* + - tests/* + - app/* + - bin/* diff --git a/.travis.yml b/.travis.yml index ce972cb7..c7870549 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,26 @@ php: - 5.3 - 5.4 - 5.5 + - 5.6 + - hhvm before_script: - wget http://getcomposer.org/composer.phar - - php composer.phar install --dev \ No newline at end of file + - php composer.phar install --dev + +script: + - cd ./tests/ + - ../vendor/bin/phpunit +# - ./vendor/bin/phpcs -n --standard=PSR2 ./SEOstats/ ./tests/ + +after_script: + - ../vendor/bin/ocular code-coverage:upload --format=php-clover ../build/logs/clover.xml + +notifications: + email: false + +matrix: + fast_finish: true + allow_failures: + - php: 5.6 + - php: hhvm diff --git a/README.md b/README.md index 1d86ce18..0fd39a14 100644 --- a/README.md +++ b/README.md @@ -348,16 +348,16 @@ More detailed examples can be found in the `./example` directory. ```php @@ -453,6 +453,6 @@ More detailed examples can be found in the `./example` directory. ## License -(c) 2010 - 2013, Stephan Schmitz eyecatchup@gmail.com +(c) 2010 - 2014, Stephan Schmitz eyecatchup@gmail.com License: MIT, http://eyecatchup.mit-license.org URL: https://github.com/eyecatchup/SEOstats diff --git a/SEOstats/Common/AutoLoader.php b/SEOstats/Common/AutoLoader.php index 6a278a28..a20eb828 100644 --- a/SEOstats/Common/AutoLoader.php +++ b/SEOstats/Common/AutoLoader.php @@ -41,23 +41,27 @@ public function __construct($namespace, $path) * * @return boolean If the loading was successful */ - public function load($class) + public function load($className) { - $class = ltrim($class, '\\'); + $class = ltrim($className, '\\'); - if (strpos($class, $this->namespace) === 0) { - $nsparts = explode('\\', $class); - $class = array_pop($nsparts); - $nsparts[] = ''; - $path = $this->path . implode(DIRECTORY_SEPARATOR, $nsparts); - $path .= str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; + if (strpos($class, $this->namespace) !== 0) { + return false; + } + + $nsparts = explode('\\', $class); + $class = array_pop($nsparts); + $nsparts[] = ''; + $path = $this->path . implode(DIRECTORY_SEPARATOR, $nsparts); + $path .= str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; - if (file_exists($path)) { - require $path; - return true; - } + if (!is_readable($path)) { + return false; } - return false; + + require $path; + + return class_exists($className,false); } /** diff --git a/SEOstats/Services/3rdparty/GTB_PageRank.php b/SEOstats/Services/3rdparty/GTB_PageRank.php index 7ce901ee..7f71fae1 100644 --- a/SEOstats/Services/3rdparty/GTB_PageRank.php +++ b/SEOstats/Services/3rdparty/GTB_PageRank.php @@ -15,16 +15,16 @@ interface tbr { // 2 toolbar server hostnames, as found in the toolbar source code. const SERVER_HOSTS = '["toolbarqueries.google.","alt1.toolbarqueries.google."]'; - + //138 toolbar server top level domains, as found in the toolbar source code. const SERVER_TLDS = '["com","ae","com.af","com.ag","com.ai","am","com.ar","as","at","com.au","az","ba","com.bd","be","bg","com.bh","bi","com.bo","com.br","bs","co.bw","com.bz","ca","cd","cg","ch","ci","co.ck","cl","com.co","co.cr","com.cu","cz","de","dj","dk","dm","com.do","com.ec","ee","com.eg","es","com.et","fi","com.fj","fm","fr","co.uk","gg","com.gi","gl","gm","gr","com.gt","com.hk","hn","hr","ht","hu","co.id","ie","co.il","co.im","co.in","is","it","co.je","com.jm","jo","co.jp","co.ke","kg","co.kr","kz","li","lk","co.ls","lt","lu","lv","com.ly","co.ma","mn","ms","com.mt","mu","mw","com.mx","com.my","com.na","com.nf","com.ni","nl","no","com.np","co.nz","com.om","com.pa","com.pe","com.ph","com.pk","pl","pn","com.pr","pt","com.py","com.qa","ro","ru","rw","com.sa","sc","se","com.sg","sh","si","sk","sm","sn","com.sv","co.th","com.tj","tm","to","com.tr","tt","com.tw","com.ua","co.ug","com.uy","co.uz","com.vc","co.ve","vg","co.vi","com.vn","co.za","co.zm"]'; // Service request path as found in the toolbar source code. const SERVER_PATH = "/tbr"; - + // Request query string as found in the toolbar source code. const QUERY_STRING = "?features=Rank&client=navclient-auto&ch=%s&q=info:%s"; - + // Google's client-specific suggestion of a prefered top level domain (as found in tb source code). const SUGGEST_TLD_URL = "https://www.google.com/searchdomaincheck?format=domain&sourceid=navclient-ff"; } @@ -37,14 +37,14 @@ class GTB_PageRank implements tbr, pref { // objects vars public $QUERY_URL, $URL_HASHES, $PREFERED_TLD, $GTB_SUGESSTED_TLD, $GTB_QUERY_STRINGS; - private $GTB_SERVER; + private $GTB_SERVER; /** __construct - Initialize a new object of the class 'GTB_PageRank'. * @access public */ public function __construct($a=NULL) { - if(NULL===$a) { - GTB_Exception::noUrl(); + if(NULL===$a) { + GTB_Exception::noUrl(); } $this->GTB_SERVER = array( // setup the toolbar server vars "host" => GTB_HELPER::_json_decode(tbr::SERVER_HOSTS), @@ -54,8 +54,8 @@ public function __construct($a=NULL) { if (!in_array(self::getPref('tld'), self::getTbrTlds() )) { GTB_Exception::invalidPref('PREFERED_TLD'); } else { - $this->PREFERED_TLD = pref::PREFERED_TLD; - $this->GTB_SUGESSTED_TLD = self::getTbrTldSuggestion(); + $this->PREFERED_TLD = pref::PREFERED_TLD; + $this->GTB_SUGESSTED_TLD = self::getTbrTldSuggestion(); } $init = self::setQueryURL($a); // setup the query url if (TRUE !== $init) { @@ -67,40 +67,21 @@ public function getPageRank() { $host = $this->GTB_SERVER['host'][0]; $tld = (strlen($this->GTB_SUGESSTED_TLD) > 0) ? $this->GTB_SUGESSTED_TLD : $this->PREFERED_TLD; $path = $this->GTB_SERVER['path']; - $tbUrl = 'http://' . $host . $tld . $path; + $tbUrl = 'http://' . $host . $tld . $path; $qStrings = self::getQueryStrings(); - if(isset($qStrings[0])) { - $PR = self::getToolbarPageRank($tbUrl . $qStrings[0]); - if ($PR !== FALSE) { - return $PR; - } else { - if (isset($qStrings[1])) { - $PR = self::getToolbarPageRank($tbUrl . $qStrings[1]); - if ($PR !== FALSE) { - return $PR; - } else { - if (isset($qStrings[2])) { - $PR = self::getToolbarPageRank($tbUrl . $qStrings[2]); - if ($PR !== FALSE) { - return $PR; - } else { - if (isset($qStrings[3])) { - $PR = self::getToolbarPageRank($tbUrl . $qStrings[3]); - if ($PR !== FALSE) { - return $PR; - } else { - return 'Failed to generate a valid hash for PR check.'; - } - } - } - } - } - } + + for ( $i=0; $i < 3; $i++ ) { + if( !isset($qStrings[$i])) { + break; } - } else { - return 'Failed to generate a valid hash for PR check.'; + $PR = self::getToolbarPageRank($tbUrl . $qStrings[$i]); + if ($PR === FALSE) { + continue; + } + return $PR; } - } + return 'Failed to generate a valid hash for PR check.'; + } public function getToolbarPageRank($toolbarUrl) { $ret = GTB_Request::_get($toolbarUrl); $pagerank = trim(substr($ret, 9)); @@ -109,52 +90,52 @@ public function getToolbarPageRank($toolbarUrl) { /** getQueryURL - Get the object query url. * @access public - */ + */ public function getQueryURL() { return $this->QUERY_URL; } /** getHash - Get a single hash key value string from object's 'URL_HASHES' array. * @access public - */ + */ public function getHash($k) { $array = $this->URL_HASHES; return $array[$k]; - } + } /** getHash - Get the object's 'URL_HASHES' array. * @access public * @return Array returns array of hash-key-pairs for the object url. - */ + */ public function getHashes() { return $this->URL_HASHES; } - + /** getQueryStrings - Get an array of formatted request query strings. * @access public - */ + */ public function getQueryStrings() { return $this->GTB_QUERY_STRINGS; - } + } /** getQueryUrls - Get the object's 'URL_HASHES' array. * @access public * @return Array returns array of all possible url combinations. - */ + */ public function getQueryUrls($limit=NULL) { $a = self::getQueryUrl(); $b = self::getHashes(); $QueryUrls = array(); - $limit = (NULL!==$limit && is_numeric($limit)) ? (int)$limit : 0; + $limit = (NULL!==$limit && is_numeric($limit)) ? (int)$limit : 0; $c = 0; //Foreach hash key value... - foreach ( $b as $k => $v ) { + foreach ( $b as $k => $v ) { //...that is a string with length > 0... if ( is_string($v) AND strlen($v) > 0 ) { - //...format a query string. + //...format a query string. $rs = sprintf(tbr::QUERY_STRING, $v, $a); //Then, foreach available toolbar hostname... foreach ( $this->GTB_SERVER['host'] as $host ) { - //...append any available top level domain... + //...append any available top level domain... foreach ($this->GTB_SERVER['tld'] as $tld) { $tbUri = 'http://'. $host . $tld . tbr::SERVER_PATH . $rs; if ( $c < $limit || $limit == 0 ) { @@ -164,35 +145,35 @@ public function getQueryUrls($limit=NULL) { } } } - } + } return (sizeof($QueryUrls)>0) ? $QueryUrls : FALSE; } /** getTbrServer - Get the Google Toolbar server vars array. * @access public * @return Array Array contains keys: 'host', 'tld', 'path'. - */ + */ public function getTbrServer() { - return $this->GTB_SERVER; - } + return $this->GTB_SERVER; + } /** getTbrHosts - Get all available host names. * @access public * @return Array Array containing all available Toolbar server host names. - */ + */ public function getTbrHosts() { - return $this->GTB_SERVER['host']; + return $this->GTB_SERVER['host']; } /** getTbrTlds - Get all available top level domains. * @access public * @return Array Array containing all available Toolbar server top level domains. - */ + */ public function getTbrTlds() { - return $this->GTB_SERVER['tld']; + return $this->GTB_SERVER['tld']; } /** getTbrTldSuggestion - Get Google's suggestion which top level domain to use. * @access public * @return Array Array containing all available Toolbar server top level domains. - */ + */ public function getTbrTldSuggestion() { $tmp = explode(".google.", GTB_Request::_get(tbr::SUGGEST_TLD_URL)); return isset($tmp[1]) ? trim($tmp[1]) : 'com'; @@ -200,17 +181,17 @@ public function getTbrTldSuggestion() { /** getTbrPath - Get the Google Toolbar Pagerank request path. * @access public * @return String - */ + */ public function getTbrPath() { - return $this->GTB_SERVER['path']; + return $this->GTB_SERVER['path']; } - + public function getPref($k) { if ($k == 'tld') { return pref::PREFERED_TLD; } } - + public function GPR_awesomeHash() { $a = self::getQueryURL(); if (NULL!==$a) { @@ -235,7 +216,7 @@ public function GPR_ieHash() { return GTB_ieHash::ieHash($a); } else { GTB_Exception::noUrl(); } } - + // setQueryURL setter function for the url key. // @return Boolean returns true if input string validated as url, else false. private function setQueryURL($a) { @@ -252,16 +233,16 @@ private function setQueryStrings($a,$b) { $qs = array(); foreach ($b as $k => $v) { //Foreach hash key value... if(is_string($v) && strlen($v) > 0) { - //...format a query string. + //...format a query string. $qs[] = sprintf(tbr::QUERY_STRING, $v, urlencode($a)); } - } + } if (sizeof($qs) > 0) { $this->GTB_QUERY_STRINGS = $qs; - return TRUE; + return TRUE; } return FALSE; - } + } }//eoc /** GTB_awesomeHash Hash a variable-length key into a 32-bit value. @@ -500,10 +481,10 @@ public static function _fmod($x, $y) { $i = floor( $x / $y ); return (int)( $x - $i * $y ); } - + // array_rand_val - Returns $n random values from array $a. public static function array_rand_val($a, $n=1) { - shuffle($a); + shuffle($a); $b = array(); for ($i=0; $i<$n; $i++) { $b[] = $a[$i]; } @@ -512,7 +493,7 @@ public static function array_rand_val($a, $n=1) { // array_rand_val_assoc - Returns $n random values from assoc array $a. public static function array_rand_val_assoc($a, $n=1) { $k = array_keys($a); - shuffle($k); + shuffle($k); $b = array(); for ($i=0; $i<$n; $i++) { $b[$k[$i]] = $a[$k[$i]]; } @@ -520,15 +501,15 @@ public static function array_rand_val_assoc($a, $n=1) { } // use regex to match values from string, if native json_decode is not available. - public static function _json_decode($a) { + public static function _json_decode($a) { if (TRUE !== function_exists('json_decode')) { $m = array(); preg_match_all('#"(.*?)"#si', $a, $m); return (isset($m[1]) && sizeof($m[1])>0) ? $m[1] : FALSE; } else { - return json_decode($a); - } - } + return json_decode($a); + } + } }//eoc /** GTB_Request Connection helper methods. * @package GTB_PageRank @@ -538,7 +519,7 @@ class GTB_Request extends GTB_PageRank { public static function _get($url) { if (!function_exists('curl_init')) { - return self::GetWithoutCurl($url); } + return self::GetWithoutCurl($url); } else { return self::GetWithCurl($url); } } @@ -565,7 +546,7 @@ private static function GetWithCurl($url) { * @package GTB_PageRank * @author Stephan Schmitz */ -class GTB_Exception extends Exception +class GTB_Exception extends Exception { // exitNoUrl - throws an exception and exits, when trying to create a new object on no input. static function noUrl() { @@ -582,23 +563,23 @@ static function tryAgain() { /* DOCUMENTATION AND TEST PROGRAM - Run './GTB_PageRank.php?man' to view the content below! */ -function print_ln() { +function print_ln() { print "--------------------------------------------------------------------------------------------------------\n"; } -function print_cbb($a="") { - if($a!="") { print_n("\nBelow, see the output of `var_dump( $a );` :"); } +function print_cbb($a="") { + if($a!="") { print_n("\nBelow, see the output of `var_dump( $a );` :"); } print "------------------------------------------------------------------------------------- CODEBLOCK BEGIN --\n"; } -function print_cbe() { +function print_cbe() { print "--------------------------------------------------------------------------------------- CODEBLOCK END --\n"; } -function print_n($a="") { +function print_n($a="") { print "$a\n"; } -function print_h($a) { +function print_h($a) { print_n(""); print_n($a); print_ln(); } if ( TRUE !== DISABLE_MAN AND isset($_GET['man']) ) : try { //init a test object $url = (isset($_GET['url']) && !empty($_GET['url'])) ? $_GET['url'] : 'http://www.nahklick.de'; $_url = new GTB_PageRank($url); - //send docs + //send docs if ( !headers_sent() ): header("Content-Type: text/plain;"); else : @@ -635,7 +616,7 @@ function print_h($a) { print_n(" [\"ie\"] => string(n) \"7xxxxxxxxxx\"} # 11-12 chars, first is 7"); print_n(" [\"PREFERED_TLD\"] => string(n) # The Toolbar top level domain *you* prefer."); print_n(" [\"GTB_SUGESSTED_TLD\"] => string(n) # The Toolbar top level domain Google suggests to you."); - print_n(" [\"GTB_QUERY_STRINGS\"] => string(n) # Array of possible path combination, based on different hashes (max. Arraysize: 4)."); + print_n(" [\"GTB_QUERY_STRINGS\"] => string(n) # Array of possible path combination, based on different hashes (max. Arraysize: 4)."); print_n(" [\"GTB_SERVER\"] => array(3) { # Array containing the Toolbar server adress parts."); print_n(" [\"host\"] => array(2) # Array containing valid toolbar host names."); print_n(" [\"tld\"] => array(138) # Array containing valid toolbar top level domains."); @@ -656,7 +637,7 @@ function print_h($a) { print_n("Output:"); print_n("\t".$_url->GPR_ieHash()."\n\t".$_url->GPR_jenkinsHash()."\n\t".$_url->GPR_jenkinsHash2()."\n\t".$_url->GPR_awesomeHash() ); print_cbe(); # END code block - print_n(); + print_n(); print_n("The same could be achieved using the `getHash(key)` method providing one of the key names"); print_n("'awesome', 'jenkins', 'jenkins2', or 'ie'."); print_cbb("\$_url->getHash('awesome')");# BEGIN code block diff --git a/SEOstats/Services/Alexa.php b/SEOstats/Services/Alexa.php index 8aef8a2f..6a5c7def 100644 --- a/SEOstats/Services/Alexa.php +++ b/SEOstats/Services/Alexa.php @@ -117,12 +117,15 @@ public static function getGlobalRank($url = false) return parent::noDataDefaultValue(); } */ - + $xpath = self::_getXPath($url); - $nodes = @$xpath->query("//*[@id='traffic-rank-content']/div/span[2]/div[1]/span/span/div/strong/a"); - return !$nodes->item(0) ? parent::noDataDefaultValue() : - self::retInt( strip_tags($nodes->item(0)->nodeValue) ); + $xpathQueryList = array( + "//*[@id='traffic-rank-content']/div/span[2]/div[1]/span/span/div/strong", + "//*[@id='traffic-rank-content']/div/span[2]/div[1]/span/span/div/strong/a" + ); + + return static::parseDomByXpathsToIntegerWithoutTags($xpath, $xpathQueryList); } /** @@ -171,8 +174,15 @@ public static function setRankingKeys($url = false) public static function getCountryRank($url = false) { $xpath = self::_getXPath($url); - $node1 = @$xpath->query("//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/h4/strong/a"); - $node2 = @$xpath->query("//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/div/strong/a"); + $node1 = self::parseDomByXpaths($xpath, array( + "//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/h4/a", + "//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/h4/strong/a", + )); + + $node2 = self::parseDomByXpaths($xpath, array( + "//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/div/strong/a", + "//*[@id='traffic-rank-content']/div/span[2]/div[2]/span/span/div/strong", + )); if ($node2->item(0)) { $rank = self::retInt(strip_tags($node2->item(0)->nodeValue)); @@ -190,19 +200,25 @@ public static function getCountryRank($url = false) public static function getBacklinkCount($url = false) { $xpath = self::_getXPath($url); - $nodes = @$xpath->query("//*[@id='linksin_div']/section/div/div[1]/span"); - return !$nodes->item(0) ? parent::noDataDefaultValue() : - self::retInt($nodes->item(0)->nodeValue); + $queryList = array( + "//section[@class='row-fluid panel-wrapper '][6]/section/div/span/div/span", + "//*[@id='linksin_div']/section/div/div[1]/span" + ); + + return static::parseDomByXpathsToInteger($xpath, $queryList); } public static function getPageLoadTime($url = false) { $xpath = self::_getXPath($url); - $nodes = @$xpath->query( "//*[@id='section-load']/div/section/p" ); - return !$nodes->item(0) ? parent::noDataDefaultValue() : - strip_tags($nodes->item(0)->nodeValue); + $queryList = array( + "//section[@class='row-fluid panel-wrapper '][9]/section/p", + "//*[@id='section-load']/div/section/p" + ); + + return static::parseDomByXpathsWithoutTags($xpath, $queryList); } /** @@ -237,13 +253,13 @@ public static function getTrafficGraph($type = 1, $url = false, $w = 660, $h = 3 /** * @return DOMXPath */ - private static function _getXPath($url) { + protected static function _getXPath($url) { $url = parent::getUrl($url); if (parent::getLastLoadedUrl() == $url && self::$_xpath) { return self::$_xpath; } - $html = self::_getAlexaPage($url); + $html = static::_getAlexaPage($url); $doc = parent::_getDOMDocument($html); $xpath = parent::_getDOMXPath($doc); @@ -252,18 +268,91 @@ private static function _getXPath($url) { return $xpath; } - private static function _getAlexaPage($url) + protected static function _getAlexaPage($url) { $domain = Helper\Url::parseHost($url); $dataUrl = sprintf(Config\Services::ALEXA_SITEINFO_URL, $domain); - $html = parent::_getPage($dataUrl); + $html = static::_getPage($dataUrl); return $html; } - private static function retInt($str) + protected static function retInt($str) { $strim = trim(str_replace(',', '', $str)); $intStr = 0 < strlen($strim) ? $strim : '0'; return intval($intStr); } + + /** + * + * @return mixed nodeValue + */ + protected static function parseDomByXpaths($xpathDom, $xpathQueryList) { + + foreach ( $xpathQueryList as $query ) { + $nodes = @$xpathDom->query($query); + + if ( $nodes->length != 0 ) { + return $nodes; + } + } + + return null; + } + + /** + * + * @return mixed nodeValue + */ + protected static function parseDomByXpathsGetValue($xpathDom, $xpathQueryList) + { + $nodes = static::parseDomByXpaths($xpathDom, $xpathQueryList); + + return ($nodes) ? $nodes->item(0)->nodeValue : null; + } + + /** + * + * @return mixed nodeValue + */ + protected static function parseDomByXpathsToInteger($xpathDom, $xpathQueryList) + { + $nodeValue = static::parseDomByXpathsGetValue($xpathDom, $xpathQueryList); + + if ($nodeValue === null) { + return parent::noDataDefaultValue(); + } + return self::retInt( $nodeValue ); + } + + /** + * + * @return mixed nodeValue + */ + protected static function parseDomByXpathsWithoutTags($xpathDom, $xpathQueryList) + { + + $nodeValue = static::parseDomByXpathsGetValue($xpathDom, $xpathQueryList); + + if ($nodeValue === null) { + return parent::noDataDefaultValue(); + } + + return strip_tags($nodeValue); + } + + /** + * + * @return mixed nodeValue + */ + protected static function parseDomByXpathsToIntegerWithoutTags($xpathDom, $xpathQueryList) + { + $nodeValue = static::parseDomByXpathsGetValue($xpathDom, $xpathQueryList); + + if ($nodeValue === null) { + return parent::noDataDefaultValue(); + } + + return self::retInt(strip_tags($nodeValue)); + } } diff --git a/SEOstats/Services/Google.php b/SEOstats/Services/Google.php index 95598e6f..8912d799 100644 --- a/SEOstats/Services/Google.php +++ b/SEOstats/Services/Google.php @@ -77,7 +77,7 @@ public static function getSearchResultsTotal($url = false) $url = parent::getUrl($url); $url = sprintf(Config\Services::GOOGLE_APISEARCH_URL, 1, $url); - $ret = parent::_getPage($url); + $ret = static::_getPage($url); $obj = Helper\Json::decode($ret); return !isset($obj->responseData->cursor->estimatedResultCount) @@ -97,7 +97,7 @@ public static function getPagespeedAnalysis($url = false) $url = sprintf(Config\Services::GOOGLE_PAGESPEED_URL, $url, Config\ApiKeys::GOOGLE_SIMPLE_API_ACCESS_KEY); - $ret = parent::_getPage($url); + $ret = static::_getPage($url); return Helper\Json::decode($ret); } @@ -107,7 +107,7 @@ public static function getPagespeedScore($url = false) $url = parent::getUrl($url); $ret = self::getPagespeedAnalysis($url); - return !$ret->score ? parent::noDataDefaultValue() : + return !isset($ret->score) || !$ret->score ? parent::noDataDefaultValue() : intval($ret->score); } @@ -129,67 +129,70 @@ public static function getSerps($query, $maxResults=100, $domain=false) $ref = 0 == $start ? 'ncr' : sprintf('search?q=%s&hl=en&prmd=imvns&start=%s0&sa=N', $q, $start); $nextSerp = 0 == $start ? sprintf('search?q=%s&filter=0', $q) : sprintf('search?q=%s&filter=0&start=%s0', $q, $start); - $curledSerp = utf8_decode( self::gCurl($nextSerp, $ref) ); + $curledSerp = utf8_decode( static::gCurl($nextSerp, $ref) ); if (preg_match("#answer[=|/]86640#i", $curledSerp)) { print('Please read: https://support.google.com/websearch/answer/86640'); exit(); } - else { - $matches = array(); - preg_match_all('#

(.*?)

#', $curledSerp, $matches); - if (!empty($matches[1])) { - $c = 0; - foreach ($matches[1] as $link) { - if (preg_match('#]*href=[\'"]?([^\'" ]+)[\'"]?[^>]*>(.*?)#', $link, $match)) { - if (!preg_match('#^https?://www.google.com/(?:intl/.+/)?webmasters#', $match[1])) { - $c++; - $resCnt = ($start * 10) + $c; - if (FALSE !== $domain) { - if (preg_match("#^{$domain}#i", $match[1])) { - $result[] = array( - 'position' => $resCnt, - 'url' => $match[1], - 'headline' => trim(strip_tags($match[2])) - ); - } - } else { - $result[$resCnt] = array( - 'url' => $match[1], - 'headline' => trim(strip_tags($match[2])) - ); - } - } - } + + + $matches = array(); + preg_match_all('#

(.*?)

#', $curledSerp, $matches); + if (empty($matches[1])) { + // No [@id="rso"]/li/h3 on currect page + $pages -= 1; + } else { + $c = 0; + foreach ($matches[1] as $link) { + if ( !preg_match('#]*href=[\'"]?([^\'" ]+)[\'"]?[^>]*>(.*?)#', $link, $match) || + preg_match('#^https?://www.google.com/(?:intl/.+/)?webmasters#', $match[1])) + { + continue; } - if ( preg_match('#id="?pnnext"?#', $curledSerp) ) { - // Found 'Next'-link on currect page - $pages += 1; - $delay += 200000; - usleep($delay); - } else { - // No 'Next'-link on currect page - $pages -= 1; + + $c++; + $resCnt = ($start * 10) + $c; + if (FALSE === $domain) { + $result[$resCnt] = array( + 'url' => $match[1], + 'headline' => trim(strip_tags($match[2])) + ); + } elseif (preg_match("#^{$domain}#i", $match[1])) { + $result[] = array( + 'position' => $resCnt, + 'url' => $match[1], + 'headline' => trim(strip_tags($match[2])) + ); } + } // foreach ($matches[1] as $link) + + + if ( preg_match('#id="?pnnext"?#', $curledSerp) ) { + // Found 'Next'-link on currect page + $pages += 1; + $delay += 200000; + usleep($delay); } else { - // No [@id="rso"]/li/h3 on currect page + // No 'Next'-link on currect page $pages -= 1; } } + if ($start == $maxResults) { $pages -= 1; } - } + } // for ($start=0; $start<$pages; $start++) return $result; } - private static function gCurl($path, $ref, $useCookie = Config\DefaultSettings::ALLOW_GOOGLE_COOKIES) + protected static function gCurl($path, $ref, $useCookie = Config\DefaultSettings::ALLOW_GOOGLE_COOKIES) { $url = sprintf('https://www.google.%s/', Config\DefaultSettings::GOOGLE_TLD); $referer = $ref == '' ? $url : $ref; $url .= $path; - $ua = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.83 Safari/535.11"; + $ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36"; if (isset($_SERVER["HTTP_USER_AGENT"]) && 0 < strlen($_SERVER["HTTP_USER_AGENT"])) { $ua = $_SERVER["HTTP_USER_AGENT"]; } diff --git a/SEOstats/Services/Mozscape.php b/SEOstats/Services/Mozscape.php index b51815fc..9a7d4dc5 100644 --- a/SEOstats/Services/Mozscape.php +++ b/SEOstats/Services/Mozscape.php @@ -22,7 +22,7 @@ class Mozscape extends SEOstats // of the URL to rank well in search engine results. public static function getPageAuthority($url = false) { - $data = self::getCols('34359738368', $url); + $data = static::getCols('34359738368', $url); return (parent::noDataDefaultValue() == $data) ? $data : $data['upa']; } @@ -31,7 +31,7 @@ public static function getPageAuthority($url = false) // of the domain of the URL to rank well in search engine results. public static function getDomainAuthority($url = false) { - $data = self::getCols('68719476736', Helper\Url::parseHost($url)); + $data = static::getCols('68719476736', Helper\Url::parseHost($url)); return (parent::noDataDefaultValue() == $data) ? $data : $data['pda']; } @@ -40,7 +40,7 @@ public static function getDomainAuthority($url = false) // http://apiwiki.moz.com/glossary#equity public static function getEquityLinkCount($url = false) { - $data = self::getCols('2048', $url); + $data = static::getCols('2048', $url); return (parent::noDataDefaultValue() == $data) ? $data : $data['uid']; } @@ -48,7 +48,7 @@ public static function getEquityLinkCount($url = false) // The number of links (equity or nonequity or not, internal or external) to the URL. public static function getLinkCount($url = false) { - $data = self::getCols('2048', $url); + $data = static::getCols('2048', $url); return (parent::noDataDefaultValue() == $data) ? $data : $data['uid']; } @@ -56,7 +56,7 @@ public static function getLinkCount($url = false) // The normalized 10-point MozRank score of the URL. public static function getMozRank($url = false) { - $data = self::getCols('16384', $url); + $data = static::getCols('16384', $url); return (parent::noDataDefaultValue() == $data) ? $data : $data['umrp']; } @@ -64,7 +64,7 @@ public static function getMozRank($url = false) // The raw MozRank score of the URL. public static function getMozRankRaw($url = false) { - $data = self::getCols('16384', $url); + $data = static::getCols('16384', $url); return (parent::noDataDefaultValue() == $data) ? $data : number_format($data['umrr'], 16); } @@ -95,7 +95,7 @@ public static function getCols($cols, $url = false) urlencode(self::_getUrlSafeSignature($expires)) ); - $ret = parent::_getPage($apiEndpoint); + $ret = static::_getPage($apiEndpoint); return (!$ret || empty($ret) || '{}' == (string)$ret) ? parent::noDataDefaultValue() @@ -118,6 +118,11 @@ private static function _hmacsha1($data, $key) return hash_hmac('sha1', $data, $key, true); } + return self::_hmacsha1Rebuild($data, $key); + } + + private static function _hmacsha1Rebuild($data, $key) + { $blocksize = 64; $hashfunc = 'sha1'; diff --git a/SEOstats/Services/OpenSiteExplorer.php b/SEOstats/Services/OpenSiteExplorer.php index 811ca6cc..64738768 100644 --- a/SEOstats/Services/OpenSiteExplorer.php +++ b/SEOstats/Services/OpenSiteExplorer.php @@ -21,7 +21,7 @@ public static function getPageMetrics($url = false) $url = parent::getUrl($url); $dataUrl = sprintf(Config\Services::OPENSITEEXPLORER_URL, 'links', '1', urlencode($url)); - $html = parent::_getPage($dataUrl); + $html = static::_getPage($dataUrl); $doc = parent::_getDOMDocument($html); $xpath = parent::_getDOMXPath(@$doc); diff --git a/SEOstats/Services/SemRush.php b/SEOstats/Services/SemRush.php index 4ba87927..f4659cb5 100644 --- a/SEOstats/Services/SemRush.php +++ b/SEOstats/Services/SemRush.php @@ -16,7 +16,7 @@ use SEOstats\Config as Config; use SEOstats\Helper as Helper; -class SEMRush extends SEOstats +class SemRush extends SEOstats { public static function getDBs() { diff --git a/SEOstats/Services/Sistrix.php b/SEOstats/Services/Sistrix.php index d6b27376..b11194ed 100644 --- a/SEOstats/Services/Sistrix.php +++ b/SEOstats/Services/Sistrix.php @@ -46,7 +46,7 @@ public static function getVisibilityIndex($url = false) $domain = Helper\Url::parseHost($url); $dataUrl = sprintf(Config\Services::SISTRIX_VI_URL, urlencode($domain)); - $html = parent::_getPage($dataUrl); + $html = static::_getPage($dataUrl); @preg_match_all('#

(.*?)<\/h3>#si', $html, $matches); return isset($matches[1][0]) ? $matches[1][0] : parent::noDataDefaultValue(); diff --git a/SEOstats/Services/Social.php b/SEOstats/Services/Social.php index aa381183..2d511f3b 100644 --- a/SEOstats/Services/Social.php +++ b/SEOstats/Services/Social.php @@ -8,7 +8,7 @@ * @author Stephan Schmitz * @copyright Copyright (c) 2010 - present Stephan Schmitz * @license http://eyecatchup.mit-license.org/ MIT License - * @updated 2013/12/16 + * @updated 2014/01/19 */ use SEOstats\SEOstats as SEOstats; @@ -36,9 +36,9 @@ public static function getGooglePlusShares($url = false) $url = parent::getUrl($url); $dataUrl = sprintf(Config\Services::GOOGLE_PLUSONE_URL, urlencode($url)); $html = parent::_getPage($dataUrl); - @preg_match_all('#c: (.*?)\.0#si', $html, $matches); + @preg_match_all('/window\.__SSR\s\=\s\{c:\s(\d+?)\./', $html, $match, PREG_SET_ORDER); - return isset($matches[1][0]) ? intval($matches[1][0]) : parent::noDataDefaultValue(); + return (1 === sizeof($match) && 2 === sizeof($match[0])) ? intval($match[0][1]) : parent::noDataDefaultValue(); } /** diff --git a/composer.json b/composer.json index da57e17e..d9cfb953 100644 --- a/composer.json +++ b/composer.json @@ -1,31 +1,56 @@ { - "name": "seostats/seostats", - "type": "library", - "description": "SEOstats is a powerful open source PHP library to request a bunch of SEO relevant metrics for any website.", - "keywords": ["SEO","Pagerank","Backlinks","Google","Mozscape","SEOmoz","Sistrix","Open Site Explorer","SEMRush","Alexa"], - "homepage": "http://github.com/eyecatchup/SEOstats", - "version": "2.5.2", - "license": "MIT", - "authors": [ - { - "name": "Stephan Schmitz", - "email": "eyecatchup@gmail.com", - "homepage": "https://github.com/eyecatchup", - "role": "Creator, Developer, Maintainer" - }, - { - "name": "SEOstats Community", - "homepage": "https://github.com/eyecatchup/seostats/contributors", - "role": "Contributor, Developer" - } - ], - "require": { - "php": ">=5.3", - "ext-curl": "*", - "ext-json": "*" + "name" : "seostats/seostats", + "type" : "library", + "description" : "SEOstats is a powerful open source PHP library to request a bunch of SEO relevant metrics for any website.", + "keywords" : [ + "SEO", + "SEOStats", + "SEO-Stats", + "Pagerank", + "Backlinks", + "Google", + "Mozscape", + "SEOmoz", + "Sistrix", + "Open Site Explorer", + "SEMRush", + "Alexa" + ], + "homepage" : "http://github.com/eyecatchup/SEOstats", + "license" : "MIT", + "authors" : [{ + "name" : "Stephan Schmitz", + "email" : "eyecatchup@gmail.com", + "homepage" : "https://github.com/eyecatchup", + "role" : "Creator, Developer, Maintainer" + }, { + "name" : "SEOstats Community", + "homepage" : "https://github.com/eyecatchup/seostats/contributors", + "role" : "Contributor, Developer" + } + ], + "require" : { + "php" : ">=5.3", + "ext-curl" : "*", + "ext-json" : "*" + }, + "require-dev" : { + "squizlabs/php_codesniffer" : "~1", + "composer/composer": "1.0.*@dev", + "phpunit/phpunit" : ">=3.7,<4", + "scrutinizer/ocular" : "~1" }, - "autoload": { - "psr-0": { "SEOstats\\": "" }, - "classmap": ["SEOstats/Services/3rdparty"] + "autoload" : { + "psr-0" : { + "SEOstats\\" : "" + }, + "classmap" : [ + "SEOstats/Services/3rdparty" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } } } diff --git a/example/get-semrush-graphs.php b/example/get-semrush-graphs.php index 9e0e72b9..f4e812a9 100644 --- a/example/get-semrush-graphs.php +++ b/example/get-semrush-graphs.php @@ -6,7 +6,7 @@ * @author Stephan Schmitz * @copyright Copyright (c) 2010 - present Stephan Schmitz * @license http://eyecatchup.mit-license.org/ MIT License - * @updated 2013/12/17 + * @updated 2014/07/26 */ // NOTE: The given path to the autoload.php assumes that you installed SEOstats via composer @@ -24,10 +24,10 @@ #require_once realpath(__DIR__ . '/SEOstats/bootstrap.php'); require_once realpath(__DIR__ . '/vendor/autoload.php'); -use \SEOstats\Services\SEMRush as SEMrush; +use \SEOstats\Services\SemRush; try { - $url = 'http://www.nahklick.de/'; + $url = 'http://www.google.de/'; // Create a new SEOstats instance. $seostats = new \SEOstats\SEOstats; @@ -38,25 +38,25 @@ /** * Print HTML code for the 'search engine traffic'-graph. */ - echo SEMrush::getDomainGraph(1); + echo SemRush::getDomainGraph(1); /** * Print HTML code for the 'search engine traffic price'-graph. */ - echo SEMrush::getDomainGraph(2); + echo SemRush::getDomainGraph(2); /** * Print HTML code for the 'number of adwords ads'-graph, * using explicitly SEMRush's data for google.de (german index). */ - echo SEMrush::getDomainGraph(3, false, 'de'); + echo SemRush::getDomainGraph(3, false, 'de'); /** * Print HTML code for the 'adwords traffic'-graph, using * explicitly SEMRush's data for google.de (german index) * and specific graph dimensions of 320*240 px. */ - echo SEMrush::getDomainGraph(4, false, 'de', 320, 240); + echo SemRush::getDomainGraph(4, false, 'de', 320, 240); /** * Print HTML code for the 'adwords traffic price'-graph, @@ -64,7 +64,7 @@ * specific graph dimensions of 320*240 px and specific * graph colors (black lines and red dots for data points). */ - echo SEMrush::getDomainGraph(5, false, 'de', 320, 240, '000000', 'ff0000'); + echo SemRush::getDomainGraph(5, false, 'de', 320, 240, '000000', 'ff0000'); } } catch (\Exception $e) { diff --git a/example/get-semrush-metrics.php b/example/get-semrush-metrics.php index 562dc46d..3b7c822e 100644 --- a/example/get-semrush-metrics.php +++ b/example/get-semrush-metrics.php @@ -6,7 +6,7 @@ * @author Stephan Schmitz * @copyright Copyright (c) 2010 - present Stephan Schmitz * @license http://eyecatchup.mit-license.org/ MIT License - * @updated 2013/12/17 + * @updated 2014/07/26 */ // NOTE: The given path to the autoload.php assumes that you installed SEOstats via composer @@ -24,10 +24,10 @@ #require_once realpath(__DIR__ . '/SEOstats/bootstrap.php'); require_once realpath(__DIR__ . '/vendor/autoload.php'); -use \SEOstats\Services\SEMRush as SEMrush; +use \SEOstats\Services\SemRush; try { - $url = 'http://www.nahklick.de/'; + $url = 'http://www.google.de/'; // Create a new SEOstats instance. $seostats = new \SEOstats\SEOstats; @@ -38,18 +38,18 @@ /** * Get the current SEMrush DomainRank metrics for the given URL. */ - print_r(SEMrush::getDomainRank()); + print_r(SemRush::getDomainRank()); /** * Get historical SEMrush DomainRank metrics for the given URL. */ - //print_r(SEMrush::getDomainRankHistory()); + //print_r(SemRush::getDomainRankHistory()); /** * Get competing domains for the given URL * and their basic SEMrush DomainRank metrics. */ - //print_r(SEMrush::getCompetitors()); + //print_r(SemRush::getCompetitors()); /** * Get organic search engine traffic data for the given URL. @@ -60,13 +60,13 @@ * Get organic search engine traffic metrics for the given URL, * using explicitly SEMrush's data for google.de (german index). */ - //print_r(SEMrush::getOrganicKeywords(false, 'de')); + //print_r(SemRush::getOrganicKeywords(false, 'de')); /** * Get an array containing explainations for the * result keys of the DomainRank metric methods. */ - //print_r(SEMrush::getParams()); + //print_r(SemRush::getParams()); } } catch (\Exception $e) { diff --git a/tests/SEOstatsTest/AbstractSEOstatsTestCase.php b/tests/SEOstatsTest/AbstractSEOstatsTestCase.php new file mode 100644 index 00000000..73f394f7 --- /dev/null +++ b/tests/SEOstatsTest/AbstractSEOstatsTestCase.php @@ -0,0 +1,126 @@ +assertDirectory = __DIR__ . '/_assert/'; + } + + public function getAssertDirectory ($file = null) + { + return $this->assertDirectory . $file?:''; + } + + + public function getStandardVersions ($version, $methode) + { + $filePattern = $this->standardVersionSubFile; + $methodeFile = sprintf($this->getAssertDirectory($filePattern), $version, $methode, 1); + + $result= array($version); + if (! file_exists($methodeFile)) { + return array($version); + } + + $fileList = new \DirectoryIterator($this->getAssertDirectory()); + $regexp = sprintf('#' . $filePattern . '$#', $version, $methode, '\d+'); + + $filtertList = new \RegexIterator($fileList, $regexp); + + $regexp = sprintf('#' . $this->standardVersionFile . '#','([^.]+)'); + foreach ($filtertList as $file) { + preg_match($regexp, $file, $matches); + $result[] = $matches[1]; + } + return $result; + } + + public function helperMakeAccessable ($object, $propertyOrMethod, $value = null) + { + if ( is_string($object) ) { + $objectClass = $object; + $object = null; + } else { + $objectClass = get_class($object); + } + + if (!isset($this->reflection[$objectClass])) { + $this->reflection[$objectClass] = new ReflectionClass($objectClass); + } + $reflection = $this->reflection[$objectClass]; + $isMethod = $reflection->hasMethod($propertyOrMethod); + + if ($isMethod) { + $reflectionSub = $reflection->getMethod($propertyOrMethod); + } else { + $reflectionSub = $reflection->getProperty($propertyOrMethod); + } + + $reflectionSub->setAccessible(true); + + if (!is_null($value)) { + if ($isMethod) { + return $reflectionSub->invokeArgs($object, $value); + } else { + $reflectionSub->setValue($object, $value); + } + } + + return $reflectionSub; + } + + public function mockGetPage($arg = null) + { + if (is_callable($arg)) { + $this->mockedSUT->staticExpects($this->any()) + ->method('_getPage') + ->will($this->returnCallback($arg)); + + return; + } + + if (! is_string($arg)) { + $arg = 2013; + } + + $standardFile = sprintf($this->getAssertDirectory() . $this->standardVersionFile, $arg); + + $this->mockedSUT->staticExpects($this->any()) + ->method('_getPage') + ->will($this->returnValue(file_get_contents($standardFile))); + } +} diff --git a/tests/SEOstatsTest/Helper/HttpRequestTest.php b/tests/SEOstatsTest/Helper/HttpRequestTest.php new file mode 100644 index 00000000..05460fd3 --- /dev/null +++ b/tests/SEOstatsTest/Helper/HttpRequestTest.php @@ -0,0 +1,79 @@ +assertSame(200, $statusCode); + } + + /** + * + * @dataProvider providerTestGetFile + */ + public function testGetFile($url, $filePath, $assertStatusCode) + { + + unlink($filePath); + $this->assertFalse(file_exists($filePath)); + $SUT = new HttpRequest(); + $statusCode = $SUT->getFile($url, $filePath); + + $this->assertTrue(file_exists($filePath)); + $this->assertSame($assertStatusCode, $statusCode); + } + + /** + * + * @dataProvider providerTestSendRequest + * @group live + */ + public function testSendRequest($url, $postData, $postJson, $assertResponse) + { + $statusCode = HttpRequest::sendRequest($url, $postData, $postJson); + + if ($assertResponse) { + $this->assertInternalType('string', $statusCode); + } else { + $this->assertFalse($statusCode); + } + } + + public function providerTestSendRequest() + { + $url = 'http://www.example.com'; + $postData = array('fieldA'=>'foo','fieldB'=>'bar'); + + return array( + array($url, $postData, false, true), + array($url, $postData, true, true), + array($url, false, false, true), + array('www.' . hash('sha512','this-domain-is-not-realy-exsist') . '.com/README.md', + $postData, false, false), + array('www.' . hash('sha512','this-domain-is-not-realy-exsist') . '.com/README.md', + $postData, true, false) + ); + } + + public function providerTestGetFile() + { + $file_buffer = $this->getAssertDirectory() . 'buffer.txt'; + $url = 'https://github.com/eyecatchup/SEOstats/blob/master'; + return array( + array($url . '/README.md', $file_buffer, true), + array('www.' . hash('sha512','this-domain-is-not-realy-exsist') . '.com/README.md', $file_buffer, true) + ); + } +} diff --git a/tests/SEOstatsTest/Helper/JsonTest.php b/tests/SEOstatsTest/Helper/JsonTest.php new file mode 100644 index 00000000..679c57b9 --- /dev/null +++ b/tests/SEOstatsTest/Helper/JsonTest.php @@ -0,0 +1,66 @@ +assertEquals($assert, $result); + } + + /** + * + * @dataProvider providerTestEncode + */ + public function testEncode($var, $assert) + { + $result = Json::encode($var); + + $this->assertEquals($assert, $result); + } + + public function providerTestDecode() + { + $jsonValid = '{"foo":"bar","baz":["foo","bar"]}'; + $arrayValid = array( + 'foo'=>'bar', + 'baz'=>array('foo','bar') + ); + + return array( + array($jsonValid, true, $arrayValid), + array($jsonValid, false, (object) $arrayValid), + ); + } + + public function providerTestEncode() + { + $jsonValid = '{"foo":"bar","baz":["foo","bar"]}'; + $arrayValid = array( + 'foo'=>'bar', + 'baz'=>array('foo','bar') + ); + + return array( + array($arrayValid, $jsonValid), + array(utf8_decode("json-with-uml-äöü"), + $this->helperJsonGivesFalseOrNull() ? 'null' : false + ), + ); + } + + public function helperJsonGivesFalseOrNull () + { + return defined('HHVM_VERSION') || version_compare(PHP_VERSION, '5.5', '<'); + } +} diff --git a/tests/SEOstatsTest/Helper/UrlTest.php b/tests/SEOstatsTest/Helper/UrlTest.php new file mode 100644 index 00000000..1167bd6f --- /dev/null +++ b/tests/SEOstatsTest/Helper/UrlTest.php @@ -0,0 +1,75 @@ +assertSame($assertResult, $result); + } + + /** + * + * @dataProvider providerTestParseHost + */ + public function testParseHost($url, $assertHost) + { + $host = Url::parseHost($url); + + $this->assertEquals($assertHost, $host); + } + + public function providerTestParseHost() + { + return array( + array('github.com','github.com'), + array('http://github.com','github.com'), + array('https://github.com','github.com'), + + array('www.github.com','www.github.com'), + array('http://www.github.com','www.github.com'), + array('https://www.github.com','www.github.com'), + + array('', false), + array('/index.php?foo=bar', false), + ); + } + + public function providerTestIsRfc() + { + return array( + array('http://github.com',true), + array('https://github.com',true), + array('https://github.com/file',true), + array('https://github.com/file#anchor',true), + array('https://github.com/#anchor',true), + array('https://github.com/file?query=value',true), + array('https://github.com/?query=value',true), + + array('http://www.github.com',true), + array('https://www.github.com',true), + array('https://www.github.com',true), + array('https://www.github.com/file',true), + array('https://www.github.com/file#anchor',true), + array('https://www.github.com/#anchor',true), + array('https://www.github.com/file?query=value',true), + array('https://www.github.com/?query=value',true), + + array('github.com',false), + array('www.github.com',false), + array('', false), + array('.', false), + array('/index.php?foo=bar', false), + ); + } +} diff --git a/tests/SEOstatsTest/SEOstatsTest.php b/tests/SEOstatsTest/SEOstatsTest.php new file mode 100644 index 00000000..b4098120 --- /dev/null +++ b/tests/SEOstatsTest/SEOstatsTest.php @@ -0,0 +1,118 @@ +SUT = new SEOstats(); + } + + /** + * @dataProvider providerTestServiceMethods + */ + public function testServiceMethods($method, $assertInstance) + { + $object = $this->SUT->{$method}(); + + $this->assertInstanceOf($assertInstance, $object); + } + + public function providerTestServiceMethods() + { + return array( + array('Alexa', 'SEOstats\Services\Alexa'), + array('Google', 'SEOstats\Services\Google'), + array('Mozscape', 'SEOstats\Services\Mozscape'), + array('OpenSiteExplorer', 'SEOstats\Services\OpenSiteExplorer'), + array('SEMRush', 'SEOstats\Services\SEMRush'), + array('Sistrix', 'SEOstats\Services\Sistrix'), + array('Social', 'SEOstats\Services\Social'), + ); + } + + public function testSetAndGetUrl() + { + $url = 'http://github.com'; + + $result = $this->SUT->getUrl($url); + $this->assertSame($url, $result); + + $this->SUT->setUrl($url); + $this->assertSame($url, $this->SUT->getUrl()); + } + + public function testSetUrlInvalid() + { + $this->setExpectedException('SEOstats\Common\SEOstatsException' ,'Invalid URL!'); + + $this->SUT->setUrl("github.com"); + } + + public function testGetHost() + { + $host = $this->SUT->getHost('http://github.com/path/file.txt'); + $this->assertEquals('github.com', $host); + } + + public function testGetDomain() + { + $domain = $this->SUT->getDomain('http://github.com/path/file.txt'); + $this->assertEquals('http://github.com', $domain); + } + + public function testGetDOMDocument() + { + $html = 'test'; + + $result = $this->helperMakeAccessable($this->SUT, '_getDOMDocument', array($html)); + $this->assertInstanceOf('DOMDocument', $result); + } + + public function testGetDOMXPath() + { + $doc = new \DOMDocument('test'); + + $result = $this->helperMakeAccessable($this->SUT, '_getDOMXPath', array($doc)); + $this->assertInstanceOf('DOMXPath', $result); + } + + /** + * + * @group live + */ + public function testGetPage() + { + $url = 'http://github.com/test'; + $result = $this->helperMakeAccessable($this->SUT, '_getPage', array($url)); + + $this->assertInternalType('string', $result); + $this->assertEquals($url, $this->SUT->getLastLoadedUrl()); + } + + public function testSetHtml() + { + $html = ''; + $this->helperMakeAccessable($this->SUT, '_lastHtml', false); + + $this->assertFalse($this->SUT->getLastLoadedHtml()); + + $result = $this->helperMakeAccessable($this->SUT, '_setHtml', array($html)); + + $this->assertSame($html, $this->SUT->getLastLoadedHtml()); + } + + public function testNoDataDefaultValue() + { + $this->markTestIncomplete(); + } +} diff --git a/tests/SEOstatsTest/Services/AbstractServiceTestCase.php b/tests/SEOstatsTest/Services/AbstractServiceTestCase.php new file mode 100644 index 00000000..7ddb410c --- /dev/null +++ b/tests/SEOstatsTest/Services/AbstractServiceTestCase.php @@ -0,0 +1,17 @@ +assertDirectory .= 'Service/'; + } +} diff --git a/tests/SEOstatsTest/Services/AlexaTest.php b/tests/SEOstatsTest/Services/AlexaTest.php new file mode 100644 index 00000000..22c65815 --- /dev/null +++ b/tests/SEOstatsTest/Services/AlexaTest.php @@ -0,0 +1,285 @@ +reflection = array(); + + $this->url = 'http://github.com'; + $this->SUT = new \SEOstats\Services\Alexa(); + $this->SUT->setUrl($this->url); + } + + /** + * + * @dataProvider providerTestSiteinfoMethodWithDiffrentVersion + * @group alexa + */ + public function testSiteinfoMethodWithDiffrentVersion ($method, $version, $type) + { + $this->mockAlexa($method); + $this->mockGetAlexaPage ($version); + $SUT = $this->mockedSUT; + $result = call_user_func(get_class($SUT) . '::' . $method, $this->url); + + $noDataDefault = $this->helperMakeAccessable($SUT, 'noDataDefaultValue', array()); + + // for the case that this version can be invalid + if ($type || $noDataDefault != $result) { + if (is_array($type)) { + foreach ($type as $arrayKey=>$arrayValueType) { + $this->assertArrayHasKey($arrayKey, $result); + $this->assertInternalType($arrayValueType, $result[$arrayKey]); + } + } elseif (is_string($type)) { + $this->assertInternalType($type, $result); + } + $this->assertNotEquals($noDataDefault, $result); + } + elseif (null === $type) { + $this->assertEquals($noDataDefault, $result); + } else { + $this->markTestSkipped(sprintf('methode %s returns an invalid result check source data version', $method)); + } + } + + /** + * + * @dataProvider providerTestGetTrafficGraph + * @group alexa + */ + public function testGetTrafficGraph($url, $paramsArray, $assertResult) + { + if ($assertResult instanceof \Exception) { + $this->setExpectedException(get_class($assertResult), $assertResult->getMessage()); + } + $result = call_user_func_array(array($this->SUT, 'getTrafficGraph'), $paramsArray); + + if (! $assertResult instanceof \Exception) { + $this->assertInternalType('string', $result); + $this->assertEquals($assertResult, $result); + } + } + + /** + * @group alexa + */ + public function testGetXPath() + { + $urlList = array( + $this->url, + 'http://www.google.de' + ); + + $reflectionMethod = $this->helperMakeAccessable($this->SUT,'_getXPath'); + $reflectionProperty = $this->helperMakeAccessable($this->SUT,'_lastLoadedUrl'); + + $result1 = $result2 = null; + + $SUT = $this->SUT; + + foreach ($urlList as $url) { + // first call + $result1 = $reflectionMethod->invoke($this->SUT, $url); + $this->assertInternalType('object', $result1); + $this->assertInstanceOf('DOMXPath', $result1); + $this->assertNotSame($result1, $result2); + + + $reflectionProperty->setValue($this->SUT, $url); + + + // secound call + $result2 = $reflectionMethod->invoke($this->SUT, $url); + $this->assertInternalType('object', $result2); + $this->assertInstanceOf('DOMXPath', $result2); + $this->assertSame($result1, $result2); + } + } + + /** + * @group alexa + */ + public function testGetAlexaPage() + { + $this->mockAlexa('_getAlexaPage'); + $this->mockGetPage(); + $reflectionMethod = $this->helperMakeAccessable($this->mockedSUT,'_getAlexaPage'); + + $result = $reflectionMethod->invoke($this->mockedSUT, $this->url); + $this->assertInternalType('string', $result); + + $this->assertRegExp('#helperMakeAccessable($this->SUT,'retInt'); + + $result = $reflectionMethod->invoke($this->SUT, $string); + $this->assertInternalType('integer', $result); + $this->assertSame($assert, $result); + } + + public function providerTestRetInt() + { + return array( + array('1234',1234), + array('12,34',1234), + array(' 1234 ',1234), + array(' 1,2,3,4 ',1234), + array('',0), + array(' , , , , ',0), + array(' ',0), + array(',,,,,',0), + ); + } + + public function providerTestGetTrafficGraph() + { + // $type = 1, $url = false, $w = 660, $h = 330, $period = 1, $html = true + $result = array(); + $result[]= array( + 'http://github.com', + array(1, false, 660, 330, 1, true), + sprintf( + 'Alexa Statistics Graph for %s', + sprintf(\SEOstats\Config\Services::ALEXA_GRAPH_URL, 't', 660, 330, 1, 'github.com'), + 660, 330, 'github.com' + ) + ); + + $paramsArray = array(); + $paramsArray[] = array( + 'width'=>660, + 'height'=>330, + 'periode'=>1, + 'typeIndex'=>1, + 'typeChar'=>'t', + 'url'=>'http://github.com', + 'domain'=>'github.com', + ); + + $paramsArray[] = array_merge($paramsArray[0], array('url'=>'http://github.com','domain'=>'github.com')); + $paramsArray[] = array_merge($paramsArray[0], array('width'=>880,'height'=>440)); + $paramsArray[] = array_merge($paramsArray[0], array('periode'=>2)); + + + $typeArray = array(0=>'',1=>'t',2=>'p',3=>'u',4=>'s',5=>'b',6=>'q',1337=>''); + + foreach ($typeArray as $typeIndex=>$typeChar) { + $paramsArray[] = array_merge($paramsArray[0], array('typeIndex'=>$typeIndex,'typeChar'=>$typeChar)); + } + + foreach ($paramsArray as $params) { + + $assertResult = $params['typeChar'] !== '' + ? sprintf(\SEOstats\Config\Services::ALEXA_GRAPH_URL, $params['typeChar'], $params['width'], $params['height'], $params['periode'], $params['domain']) + : new \Exception("Undefined variable: gtype"); + + $result[]= array( + $params['url'], + array($params['typeIndex'], $params['url'], $params['width'], $params['height'], $params['periode'], false), + $assertResult + ); + } + + return $result; + } + + public function providerTestSiteinfoMethodWithDiffrentVersion() + { + // @TODO to get the new alexa rank daily/weekly/monthly we need a svg analyse for the site comparisons< + $result = array(); + $methodList = array( + 'getPageLoadTime'=>'string', + 'getBacklinkCount'=>'integer', + 'getCountryRank'=>array('rank'=>'integer','country'=>'string'), + 'getGlobalRank'=>'integer', + 'getQuarterRank'=>'integer', + + 'getMonthlyRank'=>array('integer', null), + 'getMonthRank'=>'integer', + + 'getWeeklyRank'=>array('integer', null), + 'getWeekRank'=>'integer', + + 'getDailyRank'=>array('integer', null), + ); + + $versionList = array( + array('2013',true), + array('2014',false) # new version currently not supported + ); + + foreach ($versionList as $version) { + foreach ($methodList as $methodName=>$methodeAssertResultType) { + + $versionArray = $this->getStandardVersions($version[0], $methodName); + $iVersion = 0; + foreach($versionArray as $versionSub) { + $assertResult = $methodeAssertResultType; + + if (is_array($methodeAssertResultType) && array_key_exists(0, $methodeAssertResultType)) { + if ($version[0] == $versionSub) { + $assertResult = $methodeAssertResultType[0]; + } else { + $versionIndex = explode('-',$versionSub); + $assertResult = $methodeAssertResultType[ $versionIndex[2] - 1]; + } + } + + $result[]= array( + $methodName, + $versionSub, + $version[1] ? $assertResult : $version[1] + ); + } + } + } + + return $result; + } + + protected function mockAlexa($method, $vars=array()) + { + + $methods = array(); + switch ($method) { + case '_getXPath': + $methods = array('_getAlexaPage','_getPage'); + break; + case '_getAlexaPage': + $methods = array('_getPage'); + break; + default: + $methods = array('_getAlexaPage','_getPage'); + break; + } + + $this->mockedSUT = $this->getMock('\SEOstats\Services\Alexa', $methods); + $this->mockedSUT->setUrl(array_key_exists('url',$vars) ? $vars['url'] : $this->url); + } + + protected function mockGetAlexaPage ($version, $calledTest = null) + { + $standardFile = sprintf($this->getAssertDirectory() . $this->standardVersionFile, $version); + $this->mockedSUT->staticExpects($this->any()) + ->method('_getAlexaPage') + ->will($this->returnValue(file_get_contents($standardFile))); + } +} diff --git a/tests/SEOstatsTest/Services/Google/AbstractGoogleTestCase.php b/tests/SEOstatsTest/Services/Google/AbstractGoogleTestCase.php new file mode 100644 index 00000000..574d78f2 --- /dev/null +++ b/tests/SEOstatsTest/Services/Google/AbstractGoogleTestCase.php @@ -0,0 +1,34 @@ +mockedSUT = $this->getMock('\SEOstats\Services\Google', $methods); + $this->mockedSUT->setUrl(array_key_exists('url',$vars) ? $vars['url'] : $this->url); + } + public function setup() + { + parent::setup(); + $this->reflection = array(); + + $this->url = 'http://github.com'; + $this->SUT = new \SEOstats\Services\Google(); + $this->SUT->setUrl($this->url); + } +} diff --git a/tests/SEOstatsTest/Services/Google/GoogleApiTest.php b/tests/SEOstatsTest/Services/Google/GoogleApiTest.php new file mode 100644 index 00000000..9267033a --- /dev/null +++ b/tests/SEOstatsTest/Services/Google/GoogleApiTest.php @@ -0,0 +1,61 @@ +reflection = array(); + + $this->url = 'http://github.com'; + } + + /** + * @dataProvider providerTestSimpleMethodeTest + * @todo value controll + * @group google + * @group google-api + */ + public function testSimpleMethodeTest($method, $version, $assertValue) + { + $this->mockSUT(); + $this->mockGetPage ($version); + + $result = call_user_func(get_class($this->mockedSUT) . '::' . $method, $this->url); + + $this->assertEquals($assertValue, $result); + } + + + public function providerTestSimpleMethodeTest() + { + $failedValue = $this->helperMakeAccessable('SEOstats\Services\Google', 'noDataDefaultValue', array()); + + $version = array( + array('2014', 7200000), + array('failed', $failedValue) + ); + + $methods = array( + 'getSiteindexTotal', + 'getBacklinksTotal', + 'getSearchResultsTotal' + ); + + $result= array(); + foreach ( $methods as $m) { + foreach ($version as $v) { + + } + $result[] = array_merge(array($m), $v); + } + + return $result; + } +} diff --git a/tests/SEOstatsTest/Services/Google/GooglePagespeedTest.php b/tests/SEOstatsTest/Services/Google/GooglePagespeedTest.php new file mode 100644 index 00000000..5bea1b44 --- /dev/null +++ b/tests/SEOstatsTest/Services/Google/GooglePagespeedTest.php @@ -0,0 +1,50 @@ +reflection = array(); + + $this->url = 'http://github.com'; + } + + /** + * @dataProvider providerTestSimpleMethodeTest + * @todo value controll + * @group google + * @group google-pagespeed + */ + public function testSimpleMethodeTest($method, $version, $assertValue) + { + $this->mockSUT(); + $this->mockGetPage ($version); + + $result = call_user_func(get_class($this->mockedSUT) . '::' . $method, $this->url); + + $this->assertEquals($assertValue, $result); + } + + + public function providerTestSimpleMethodeTest() + { + $failedValue = $this->helperMakeAccessable('SEOstats\Services\Google', 'noDataDefaultValue', array()); + $pagespeedValue = json_decode(file_get_contents($this->getAssertDirectory('google-pagespeed-2014.json'))); + + $result[] = array('getPagespeedAnalysis', '2014', $pagespeedValue); + $result[] = array('getPagespeedAnalysis', 'failed', (object) array()); + + $result[] = array('getPagespeedScore', '2014', 90); + $result[] = array('getPagespeedScore', 'failed', $failedValue); + + + return $result; + } +} diff --git a/tests/SEOstatsTest/Services/Google/GoogleSearchTest.php b/tests/SEOstatsTest/Services/Google/GoogleSearchTest.php new file mode 100644 index 00000000..d63b720f --- /dev/null +++ b/tests/SEOstatsTest/Services/Google/GoogleSearchTest.php @@ -0,0 +1,157 @@ +SUT->getPageRank(); + + $this->assertInternalType('string', $result); + $this->assertGreaterThanOrEqual(0, $result); + } + + /** + * @dataProvider providerTestGoogleCurl + * @group google + * @group google-search + * @group live + */ + public function testGoogleCurl($args, $status) + { + $result = $this->helperMakeAccessable($this->SUT, 'gCurl', $args); + + if ($status) { + $this->assertInternalType('string', $result); + $this->assertTrue(strlen($result) >= 1); + + } else { + $this->assertFalse($result); + } + } + + /** + * @dataProvider providerTestGetSerps + * @todo value controll + * @group google + * @group google-search + */ + public function testGetSerps($args, $version, $assertResultCount) + { + $this->mockSUT('getSerps'); + $this->mockGCurl ($version); + + $result = $this->helperMakeAccessable($this->mockedSUT, 'getSerps', $args); + + $this->assertEquals($assertResultCount, count($result)); + } + + + public function providerTestGoogleCurl() + { + $query = rawurlencode('github.com'); + + $result = array(); + + // @todo implement cookie support in tests + $result[] = array(array(# $path, $ref, $useCookie + sprintf('search?q=%s&filter=0', $query), + 'ncr', + false + ), + true); + $result[] = array(array(# $path, $ref, $useCookie + sprintf('search?q=%s&filter=0', $query), + '', + false + ), + true); + + return $result; + } + + + public function providerTestGetSerps() + { + // query, $maxResults=100, $domain=false + $query = 'github.com'; + + $args = array( $query, 10, false ); + $result[] = array($args, '2014', 15); // github.com result gives more than 10 results on first page + $result[] = array($args, 'failed', 0); + + // @TODO fix domain filter regexp + // $args = array( $query, 10, 'github.com' ); + // $result[] = array($args, '2014', 0); + // $result[] = array($args, 'failed', 0); + + + + // @TODO add support for 4, 15 , 25 maxResult to + // $args = array( $query, 15, false ); + // $result[] = array($args, '2014', 15); + // $result[] = array($args, 'failed', 0); + + // @TODO fix domain filter regexp + // $args = array( $query, 15, 'github.com' ); + // $result[] = array($args, '2014', 15); + // $result[] = array($args, 'failed', 0); + + + + + // @TODO fix domain filter regexp + // $args = array( $query, 10, 'github.com' ); + // $result[] = array($args, '2014', 0); + // $result[] = array($args, 'failed', 0); + + $args = array( $query, 20, false ); + $result[] = array($args, '2014', 25); + $result[] = array($args, 'failed', 0); + + // @TODO fix domain filter regexp + // $args = array( $query, 20, 'github.com' ); + // $result[] = array($args, '2014', 20); + // $result[] = array($args, 'failed', 0); + + + return $result; + } + + public function setUp () + { + parent::setUp(); + $this->called = 1; + } + + protected function mockGCurl ($version) + { + $standardFile = $this->getAssertDirectory() . $this->standardVersionFile; + $that = $this; + + $this->mockedSUT->staticExpects($this->any()) + ->method('gCurl') + ->will($this->returnCallback(function() use ($standardFile, $version, $that) { + $file = sprintf($standardFile, $version . '-page-' . $that->called); + + if (!file_exists($file)) { + $file = sprintf($standardFile, $version); + } + $that->called++; + + return file_get_contents($file); + })); + } +} diff --git a/tests/SEOstatsTest/Services/MozscapeTest.php b/tests/SEOstatsTest/Services/MozscapeTest.php new file mode 100644 index 00000000..c4b2f788 --- /dev/null +++ b/tests/SEOstatsTest/Services/MozscapeTest.php @@ -0,0 +1,166 @@ +reflection = array(); + + $this->url = 'http://github.com'; + $this->SUT = new \SEOstats\Services\Mozscape(); + $this->SUT->setUrl($this->url); + } + + public function providerTestGetMethodeMethods() + { + $SUT = '\SEOstats\Services\Mozscape'; + + $validValue = array('foo'=>'bar'); + $inValidValue = $this->helperMakeAccessable($SUT, 'noDataDefaultValue', array()); + + $result = array(); + $float = array( + 100.12345678901234567890, + 100.1234567890123456 + ); + + $result[]= array('getMozRankRaw', '16384', array('umrr'=>$float[0]), $float[1]); + $result[]= array('getMozRankRaw', '16384', $inValidValue, $inValidValue); + + $result[]= array('getMozRank', '16384', array('umrp'=>'foo'), 'foo'); + $result[]= array('getMozRank', '16384', $inValidValue, $inValidValue); + + + $result[]= array('getLinkCount', '2048', array('uid'=>'foo'), 'foo'); + $result[]= array('getLinkCount', '2048', $inValidValue, $inValidValue); + + $result[]= array('getEquityLinkCount', '2048', array('uid'=>'foo'), 'foo'); + $result[]= array('getEquityLinkCount', '2048', $inValidValue, $inValidValue); + + + $result[]= array('getDomainAuthority', '68719476736', array('pda'=>'foo'), 'foo'); + $result[]= array('getDomainAuthority', '68719476736', $inValidValue, $inValidValue); + + + $result[]= array('getPageAuthority', '34359738368', array('upa'=>'foo'), 'foo'); + $result[]= array('getPageAuthority', '34359738368', $inValidValue, $inValidValue); +#*/ + + return $result; + } + + /** + * @dataProvider providerTestGetMethodeMethods + */ + public function testGetMethodeMethods($methode, $metricCode, $callbackReturn, $assertResult) + { + $this->mockSUT(); + $this->mockGetCols(array($metricCode, null), $callbackReturn); + + $result = call_user_func(array($this->mockedSUT,$methode), null); + $this->assertEquals($assertResult, $result); + } + + /** + * @dataProvider providerTestGetCols + */ + public function testGetCols($callbackReturn, $assertResult) + { + $this->mockSUT('getCols'); + $that = $this; + $this->mockGetPage(function($url) use($callbackReturn, $that) { + $parse = parse_url($url); + parse_str($parse['query'], $query); + + $assertSignature = $that->helperMakeAccessable( + $that->SUT, + '_getUrlSafeSignature', + array($query['Expires'], basename($parse['path'])) + ); + + $that->assertEquals($assertSignature, $query['Signature']); + + return $callbackReturn; + }); + + $result = $this->mockedSUT->getCols(1337); + $this->assertEquals($assertResult, $result); + } + + public function testGetUrlSafeSignature() + { + $expires = 1405036732; + + $sig = $this->helperMakeAccessable($this->SUT,'_getUrlSafeSignature', array($expires)); + + $this->assertEquals('BH4/rZyS0Hv8/3UMU6MnOMGD5Ow=', $sig); + } + + public function testHmacSha1() + { + $data = hash('sha512','foo'); + $key = md5('bar'); + + $assert = hash_hmac('sha1', $data, $key, true); + $value1 = $this->helperMakeAccessable($this->SUT,'_hmacsha1', array($data, $key)); + $value2 = $this->helperMakeAccessable($this->SUT,'_hmacsha1Rebuild', array($data, $key)); + + $this->assertEquals($assert, $value1); + $this->assertEquals($assert, $value2); + $this->assertEquals($value1, $value2); + } + + public function providerTestGetCols() + { + $SUT = '\SEOstats\Services\Mozscape'; + + $validValue = array('foo'=>'bar'); + $inValidValue = $this->helperMakeAccessable($SUT, 'noDataDefaultValue', array()); + + return array( + array('', $inValidValue), + array('{}', $inValidValue), + array(false, $inValidValue), + array('invalid json', null), + array(json_encode($validValue), $validValue), + ); + } + + protected function mockSUT($method=null, $vars=array()) + { + + $methods = array(); + switch ($method) { + case 'getCols': + $methods = array('_getPage'); + break; + default: + $methods = array('getCols'); + break; + } + + $this->mockedSUT = $this->getMock('\SEOstats\Services\Mozscape', $methods); + $this->mockedSUT->setUrl(array_key_exists('url',$vars) ? $vars['url'] : $this->url); + } + + + + protected function mockGetCols ($assertParams, $returnValue) + { + $that = $this; + $this->mockedSUT->staticExpects($this->any()) + ->method('getCols') + ->will($this->returnCallback(function ($cols, $url = null) use($assertParams, $returnValue, $that) { + + $that->assertEquals($assertParams[0], $cols); + $that->assertEquals($assertParams[1], $url); + + return $returnValue; + })); + } +} diff --git a/tests/SEOstatsTest/Services/OpenSiteExplorerTest.php b/tests/SEOstatsTest/Services/OpenSiteExplorerTest.php new file mode 100644 index 00000000..1e3a9ed7 --- /dev/null +++ b/tests/SEOstatsTest/Services/OpenSiteExplorerTest.php @@ -0,0 +1,65 @@ +reflection = array(); + + $this->url = 'http://github.com'; + } + + /** + * @dataProvider providerTestGetPageMetrics + * @todo value controll + */ + public function testGetPageMetrics($version, $status) + { + $this->mockSUT(); + $this->mockGetPage ($version); + + $result = call_user_func(get_class($this->mockedSUT) . '::getPageMetrics', $this->url); + + if ($status) { + $assertPropertyArray = array('domainAuthority','pageAuthority','justDiscovered', + 'justDiscovered', 'linkingRootDomains', 'totalLinks'); + + $assertSubPropertyArray = array('result','unit','descr'); + + foreach ($assertPropertyArray as $assertProperty ) { + $this->assertTrue( isset($result->{$assertProperty}) ); + + foreach ($assertSubPropertyArray as $assertSubProperty ) { + $this->assertTrue( isset($result->{$assertProperty}->{$assertSubProperty}) ); + } + } + + } else { + $this->assertEquals($this->helperMakeAccessable($this->mockedSUT, 'noDataDefaultValue', array()), $result); + } + } + + + public function providerTestGetPageMetrics() + { + return array( + array('2014', true), + array('failed', false) + ); + } + + protected function mockSUT($method=null, $vars=array()) + { + $methods = array('_getPage'); + + $this->mockedSUT = $this->getMock('\SEOstats\Services\OpenSiteExplorer', $methods); + $this->mockedSUT->setUrl(array_key_exists('url',$vars) ? $vars['url'] : $this->url); + } +} diff --git a/tests/SEOstatsTest/Services/SemRushTest.php b/tests/SEOstatsTest/Services/SemRushTest.php new file mode 100644 index 00000000..c1b32ffb --- /dev/null +++ b/tests/SEOstatsTest/Services/SemRushTest.php @@ -0,0 +1,73 @@ +reflection = array(); + + $this->url = 'http://github.com'; + } + + public function testGetDBs() + { + $this->markTestIncomplete(); + } + + public function testGetParams() + { + $this->markTestIncomplete(); + } + + public function testGetDomainRank() + { + $this->markTestIncomplete(); + } + + public function testGetDomainRankHistory() + { + $this->markTestIncomplete(); + } + + public function testGetOrganicKeywords() + { + $this->markTestIncomplete(); + } + + public function testGetCompetitors() + { + $this->markTestIncomplete(); + } + + public function testGetDomainGraph() + { + $this->markTestIncomplete(); + } + + public function testGetApiData() + { + $this->markTestIncomplete(); + } + + public function testGetBackendUrl() + { + $this->markTestIncomplete(); + } + + public function testGetWidgetUrl() + { + $this->markTestIncomplete(); + } + + public function testExc() + { + $this->markTestIncomplete(); + } +} diff --git a/tests/SEOstatsTest/Services/SistrixTest.php b/tests/SEOstatsTest/Services/SistrixTest.php new file mode 100644 index 00000000..8a9bb011 --- /dev/null +++ b/tests/SEOstatsTest/Services/SistrixTest.php @@ -0,0 +1,64 @@ +reflection = array(); + + $this->url = 'http://github.com'; + } + + /** + * @dataProvider providerTestGetVisibilityIndex + * @todo value controll + */ + public function testGetVisibilityIndex($version, $status) + { + $this->mockSUT(); + $this->mockGetPage ($version); + + $result = call_user_func(get_class($this->mockedSUT) . '::getVisibilityIndex', $this->url); + + if ($status) { + $assertValue ='h3 foo1'; + + } else { + $assertValue = $this->helperMakeAccessable($this->mockedSUT, 'noDataDefaultValue', array()); + } + + $this->assertEquals($assertValue, $result); + } + + + public function providerTestGetPageMetrics() + { + return array( + array('2014', true), + array('failed', false) + ); + } + + public function providerTestGetVisibilityIndex() + { + return array( + array('2013', true), + array('failed', false) + ); + } + + protected function mockSUT($method=null, $vars=array()) + { + $methods = array('_getPage'); + + $this->mockedSUT = $this->getMock('\SEOstats\Services\Sistrix', $methods); + $this->mockedSUT->setUrl(array_key_exists('url',$vars) ? $vars['url'] : $this->url); + } +} diff --git a/tests/SEOstatsTest/Services/SocialTest.php b/tests/SEOstatsTest/Services/SocialTest.php new file mode 100644 index 00000000..c8f1691b --- /dev/null +++ b/tests/SEOstatsTest/Services/SocialTest.php @@ -0,0 +1,78 @@ +reflection = array(); + + $this->url = 'http://github.com'; + } + + public function testGetGoogleShares() + { + $this->markTestIncomplete(); + } + + public function testGetGooglePlusShares() + { + $this->markTestIncomplete(); + } + + public function testGetFacebookShares() + { + $this->markTestIncomplete(); + } + + public function testGetTwitterShares() + { + $this->markTestIncomplete(); + } + + public function testGetDeliciousShares() + { + $this->markTestIncomplete(); + } + + public function testGetDeliciousTopTags() + { + $this->markTestIncomplete(); + } + + public function testGetDiggShares() + { + $this->markTestIncomplete(); + } + + public function testGetLinkedInShares() + { + $this->markTestIncomplete(); + } + + public function testGetPinterestShares() + { + $this->markTestIncomplete(); + } + + public function testGetStumbleUponShares() + { + $this->markTestIncomplete(); + } + + public function testGetVKontakteShares() + { + $this->markTestIncomplete(); + } + + public function testGetXingShares() + { + $this->markTestIncomplete(); + } +} diff --git a/tests/SEOstatsTest/_assert/Service/AbstractServiceTestCase.php b/tests/SEOstatsTest/_assert/Service/AbstractServiceTestCase.php new file mode 100644 index 00000000..7ddb410c --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/AbstractServiceTestCase.php @@ -0,0 +1,17 @@ +assertDirectory .= 'Service/'; + } +} diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getDailyRank-1.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getDailyRank-1.html new file mode 100644 index 00000000..03f9e578 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getDailyRank-1.html @@ -0,0 +1,67 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
Yesterday1 +0
7 day1 +0
1 month1 +-1 Change in Traffic Rank over the trailing 1 month period (A negative change means the site is getting more popular)
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getDailyRank-2.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getDailyRank-2.html new file mode 100644 index 00000000..32ef387f --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getDailyRank-2.html @@ -0,0 +1,40 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getMonthlyRank-1.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getMonthlyRank-1.html new file mode 100644 index 00000000..c68deb87 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getMonthlyRank-1.html @@ -0,0 +1,48 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
1 month1 +-1 Change in Traffic Rank over the trailing 1 month period (A negative change means the site is getting more popular)
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getMonthlyRank-2.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getMonthlyRank-2.html new file mode 100644 index 00000000..32ef387f --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getMonthlyRank-2.html @@ -0,0 +1,40 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getWeeklyRank-1.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getWeeklyRank-1.html new file mode 100644 index 00000000..3b211bff --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getWeeklyRank-1.html @@ -0,0 +1,57 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
7 day1 +0
1 month1 +-1 Change in Traffic Rank over the trailing 1 month period (A negative change means the site is getting more popular)
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getWeeklyRank-2.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getWeeklyRank-2.html new file mode 100644 index 00000000..32ef387f --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-getWeeklyRank-2.html @@ -0,0 +1,40 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-2.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-2.html new file mode 100644 index 00000000..32ef387f --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-2.html @@ -0,0 +1,40 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-3.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-3.html new file mode 100644 index 00000000..c68deb87 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-3.html @@ -0,0 +1,48 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
1 month1 +-1 Change in Traffic Rank over the trailing 1 month period (A negative change means the site is getting more popular)
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-4.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-4.html new file mode 100644 index 00000000..3b211bff --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-4.html @@ -0,0 +1,57 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
7 day1 +0
1 month1 +-1 Change in Traffic Rank over the trailing 1 month period (A negative change means the site is getting more popular)
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-5.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-5.html new file mode 100644 index 00000000..03f9e578 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013-setRankingKeys-5.html @@ -0,0 +1,67 @@ + + + + + +Google.com Site Info + + + + +
+
+

Traffic rank for google.com:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 Traffic RankChange
Yesterday1 +0
7 day1 +0
1 month1 +-1 Change in Traffic Rank over the trailing 1 month period (A negative change means the site is getting more popular)
3 month2 ++1 Change in Traffic Rank over the trailing 3 month period (A positive change means the site is getting less popular)
+
+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013.html new file mode 100644 index 00000000..f8a293f7 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2013.html @@ -0,0 +1,2437 @@ + + + + + + + + + + + + + +Google.com Site Info + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+ + + +
+

google.com

+Google +
+
+ + +
+Add to Comparison +
+ + +  + + +
+ +
+

Sign up to access comparisons feature.

+
+

Easily compare sites on traffic, engagement, reputation and demographics metrics and see who is winning.


+

Features

+
    +
  • - Compare up to 10 sites.
  • +
  • - Create unlimited lists
  • +
  • - Global Rank comparisons
  • +
  • - Traffic, engagement, reputation
      and demographics comparisons.
      (PRO only) +
+

+

Sign up for a FREE account and start comparing sites.

+Create an account +
+
+ +
+

Already have a subscription?

+

Login with your Alexa Account

+
+
+
+ + +
+
+ + + + + + +
+
+
+
+ + + + + +Forgot your password? + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Customize your site listing with your logo, plus add links back to your +site and much more! Enhanced Site Listings are just one of the features +you get with an Alexa PRO subscription. Learn +more about Enhanced Site Listings. +
+
+ +

I want an Alexa PRO subscription.

+ +
+ +
+

I have an Alexa PRO subscription.

+ +Sign In + +
+
+
+
+ +
+
+
+
+ +

How popular is google.com?

+

+
+ + +
+

+Alexa Traffic Ranks
+The global and country traffic ranks show how popular a site is relative to other sites. +

+ +

+Unique Visitors and Pageviews
+The number of people who visit this site and the number of pages they view. +Site owners who install the Alexa Certify Code on their website can choose +to display their Certified Metrics, such as Monthly Unique Visitors and +Pageviews, if they wish. Coming soon, estimated metrics will be displayed +for many sites if Certified Metrics are not available. +

+ +
+
+
+
+
+
+ +
+

+Alexa Traffic Ranks +

+

How is this site ranked relative to other sites?

+
+
+ +
+
+ +
+ + +

Global Rank + + + +Alexa Traffic Rank
An estimate of +this site's popularity.

The rank is calculated using a combination of average daily visitors to +this site and pageviews on this site over the past 3 months. The site with the highest combination +of visitors and pageviews is ranked #1.

Updated Daily
+ +
+

+
+Global rank icon2 +1 +
+ +
    +
  • -1%
  • +
+
+
+
+
+
+ + +

Rank in United States + + + +Traffic Rank in Country
An estimate of this site's popularity in a specific country.

+The rank by country is calculated using a combination of average daily visitors +to this site and pageviews on this site from users from that country over the +past month. The site with the highest combination of visitors and pageviews +is ranked #1 in that country.

Updated Daily
+ +
+

+
+United States Flag1 +  +
+ +
    +
  • %
  • +
+
+
+
+
+
+
+
+
+ +Did you know? You can get the most accurate rank possible by certifying your site's metrics. +Find out how. + +
+
+
+
+
+
+
+ +

How engaged are visitors to google.com?

+

+
+ + +
+

+How engaged are visitors to this site?
+Engagement metrics help you understand how interested a site's visitors are with the site's content. The metrics are updated daily based on the trailing 3 months. +

+

+Bounce Rate (%)
+Percentage of visits to the site that consist of a single pageview. +

+

+Daily Pageviews per Visitor
+Estimated daily unique pageviews per visitor on the site. +

+

+Daily Time on Site
+Estimated daily time on site (mm:ss) per visitor to the site. +

+
+
+
+
+
+
+ + + +

Bounce Rate

+
+18.00% +7.00% +
+ +
    +
  • 7%
  • +
+
+
+
+
+ + + +

Daily Pageviews per Visitor

+
+16.89 +5.23% +
+ +
    +
  • 5.23%
  • +
+
+
+
+
+ + + +

Daily Time on Site

+
+14:53 +4.00% +
+ +
    +
  • -4%
  • +
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+ +

Who visits google.com?

+

+
+ + +
+

+Audience Demographics
+The audience demographics data comes from voluntary demographics information submitted by people in our global traffic panel. The data is for the past 12 months, updated monthly. +Learn more +

+

The demographics data consists of:

+

+

+

+

+Audience Geography
+The audience geography data describes where visitors to this site over the past month are located, and how the site is ranked in popular countries. If a country is not listed, it is because Alexa does not have enough data for this site to rank/measure the site's popularity among that country's online population. These metrics are updated monthly. +

+
+
+
+
+
+
+

+Audience Demographics +

+

How similar is this site's audience to the general internet population?

+
+
+
+ +
+
+Gender +
+
+ +Male + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among Males is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+ +Female + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among Females is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+
+ +
+
+Education +
+
+ +No College + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among people who did not go to college is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+ +Some College + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among people some college education is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+ +Graduate School + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among people who went to graduate school is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+ +College + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among people who went to college is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+
+ +
+
+Browsing Location +
+
+ +Home + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among people browsing from home is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+ +School + + +
+ + +  + + + + +  + + + + + + + +Relative to the general internet population, people browsing from school are under-represented at this site.

Confidence: high
+ +
+
+
+
+
+
+ +Work + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among people browsing from work is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+
+
+
+Subscribe to Alexa Pro to view all demographics including age, income, ethnicity and children. +View More +
+
+ + +  + + +
+ +
+

Subscribe to view all demographics

+
+

Gain access to:

+
    +
  • - Age, income, children, ethnicity
      in additon to gender, education and browsing location.
  • +
  • - Comparisons of website demographics
  • +
+
+
+

Subscribe to Alexa Pro Insight Plan to
view all demographics.

+Subscribe +
+
+ +
+

Already have a subscription?

+

Login with your Alexa Account

+
+
+
+ + +
+
+ + + + + + +
+
+
+
+ + + + + +Forgot your password? + + +
+
+
+
+
+
+
+
+
+
+
+
+

+Audience Geography +

+

Where are this site's visitors located?

+
+
+ +
+

Visitors by Country

+
+

You need Flash 8 to view the map.

+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CountryPercent of VisitorsRank in Country
United States Flag  United States 30.2% 1
India Flag  India 8.9% 1
Brazil Flag  Brazil 3.4% 3
Russia Flag  Russia 3.3% 4
United Kingdom Flag  United Kingdom 2.8% 2
Japan Flag  Japan 2.6% 5
Iran Flag  Iran 2.4% 1
Germany Flag  Germany 2.4% 4
Indonesia Flag  Indonesia 2.4% 1
France Flag  France 2.2% 2
+
+
+
+
+
+ + +
+
+
+
+ +

What sites link to google.com?

+

+
+ + +
+

The "Sites Linking In" count shows the number of sites that Alexa found that link to this site. +For more information please see this explanation of +how Alexa determines the number of sites linking in.

+

The complete list of sites linking to this site is available to Alexa Pro subscribers.

+
+
+
+
+
+
+
+
Total Sites Linking In
+4,272,563 +
+ + + + + + + + + + + + + + + + + + + + +
SiteGlobal RankPage
1. amazon.com 6 amazon.com/-/e/B001JS4V8E
2. twitter.com 11 blog.de.twitter.com/2010/04/wir-wachse...
3. linkedin.com 14 apply.linkedin.com/documents/libraries...
4. google.co.in 13 bks0.books.google.co.in
5. google.fr 35 adwords.google.fr/support/aw/bin/searc...
+
+Subscribe to Alexa Pro to view all 4,272,563 sites linking in. +View More +
+
+ + +  + + +
+ +
+

Subscribe to view all sites linking in

+
+

With any Alexa Pro plan you can:

+
    +
  • - Get the full list of sites linking in for any site
  • +
  • - Benchmark your link-building efforts
  • +
  • - See who is linking to your competitors
  • +
+
+

Subscribe to Alexa Pro to
view all sites linking in

+Subscribe +
+
+ +
+

Already have a subscription?

+

Login with your Alexa Account

+
+
+
+ + +
+
+ + + + + + +
+
+
+
+ + + + + +Forgot your password? + + +
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+ +

+Where do visitors go on google.com? +

+

+
+ + +
+

The table shows the top subdomains for this site ordered by the percentage of visitors +that visited the subdomain over a month. Note that the percentages can add up to more than +100% because a visitor can visit multiple subdomains during the month.

+

Updated Monthly.

+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SubdomainPercent of Visitors
google.com 72.24%
mail.google.com 48.66%
accounts.google.com 34.25%
docs.google.com 12.65%
plus.google.com 10.59%
drive.google.com 6.65%
translate.google.com 6.23%
maps.google.com 5.57%
support.google.com 5.30%
adwords.google.com 3.49%
play.google.com 2.98%
news.google.com 2.27%
developers.google.com 1.06%
sites.google.com 1.05%
productforums.google.com 1.01%
code.google.com 0.96%
url.google.com 0.76%
feedburner.google.com 0.76%
groups.google.com 0.71%
ejabat.google.com 0.49%
admin.google.com 0.37%
picasaweb.google.com 0.35%
encrypted.google.com 0.18%
prod.google.com 0.02%
+
+
+
+
+
+
+
+ +

How fast does google.com load?

+

+
+ + +
+

The reported load time for a website is the median time it takes to load +pages from that site in a real users' web browsers.

+

Alexa takes the median of all the page load times we observe for a site +and then compares that to the same figure for all other sites. For example, +a site in the 98th percentile (Very Fast) has a median load time faster than +98% of all measured sites, while a site in the 2nd percentile (Very Slow) +loads more quickly than only 2% of all sites and is slower than 97% of all sites.

+

The load time of an individual page is how long it takes for the DOM - +the structure of the page - to be loaded. This time doesn't include the time +to load all images and stylesheets, for example.

+

The load time metric is updated monthly.

+
+
+
+
+

Average (1.439 Seconds), 52% of sites are slower.

+
+
+
+
+
+
+
+ +

Where can I find more info about google.com?

+

+
+ + +
+

+Site Description
+A short description of the site. +

+

+Contact
+How to contact the owner of the site. +

+
+
+
+
+
+
+ +

Site Description

+

Enables users to search the world's information, including webpages, images, and videos. Offers unique features and search technology.

+ +
+ +

Contact

+
unlisted
dns-admin [at] google.com

+
+
+ +
+
+
+
+
+
+
+ +

How can I get deeper insight?

+

By subscribing to Alexa Pro you can gain deeper insight on your traffic and search engine optimizations.

+
+ + +
+

Alexa Pro subscriptions for site owners give you metrics, +tools and analysis to increase your web traffic and succeed online. +We give you accurate traffic metrics, automated site scans, lists of sites linking in, +SEO recommendations, and much more.

+
+
+
+
+
+
    +
  • +

    SEO Score

    +

    Alexa will audit your site regularly and give you reports with actionable recommendations.

    +
  • +
  • +

    Accurate Metrics

    +

    Get Alexa Traffic Rank and other key performance metrics, certified for accuracy by Alexa.

    +
  • +
  • +

    Uptime Monitor

    +

    Know what percent of the time your site is up, and more importantly when it was down.

    +
  • +
+ +
+
+
+ + + +
+
+ +
+ +
+

Try Alexa PRO Basic: One Month Free

+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+ + + + + + + + + + + + + diff --git a/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2014.html b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2014.html new file mode 100644 index 00000000..18f7e842 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/alexa-siteinfo-2014.html @@ -0,0 +1,6718 @@ + + + + + + + + + + + +github.com Site Overview + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + +
+
+
+
+ +
+
+
+
+
+
+

Competitive Intelligence

+
+
+
+ +
+
+ +
+
+

Site Overview

+
+
+
+ + + +
+
+ +

+ +
+
+ +Is this your site? +Certify your site's metrics. + +
+
+
+
+
+
+ +
+ + + +
+
+
+
+

Certified Site Metrics are metrics that are directly-measured from the website +instead of estimated. The website owner has installed an Alexa Certify Code on +the pages of their site and chosen to show the metrics publicly. +

+
For the website owner Certified Metrics provide:
+
    +
  • A more accurate Alexa Rank
  • +
  • A private metrics Dashboard for On-Site Analytics
  • +
  • The ability to publish unique visitor and pageview counts if desired
  • +
+

Certified Metrics are available with all Alexa Pro plans.

+ +
+
+

Not all websites implement our on-site analytics and publish the results. +For these sites, we show estimated metrics based on traffic patterns across the web as a whole. +We identify these patterns by looking at the activity of millions of web users throughout the world, +and using data normalization to correct for any biases.

+

The more traffic a site gets, the more data we have to calculate estimated metrics. +Estimates are more reliable the closer a site is to being ranked #1. Global traffic ranks of 100,000+ +are subject to large fluctuations and should be considered rough estimates.

+

If a site has Certified Metrics instead of estimated, that means its owner has installed code allowing us to directly measure their traffic. +These metrics have a greater level of accuracy, no matter what the ranking.

+

Learn more about Alexa's Data

+
+ +

How popular is github.com?

+
+
+ +
+

+Alexa Traffic Ranks +

+

How is this site ranked relative to other sites?

+
+
+ +
+
+ +
+ + +

Global Rank + + + +Alexa Traffic Rank
An estimate of +this site's popularity.

The rank is calculated using a combination of average daily visitors to +this site and pageviews on this site over the past 3 months. The site with the highest combination +of visitors and pageviews is ranked #1.

Updated Daily
+ +
+

+
+Global rank icon178 +6 +
+ +
    +
  • -6%
  • +
+
+
+
+
+
+ + +

Rank in United States + + + +Traffic Rank in Country
An estimate of this site's popularity in a specific country.

+The rank by country is calculated using a combination of average daily visitors +to this site and pageviews on this site from users from that country over the +past month. The site with the highest combination of visitors and pageviews +is ranked #1 in that country.

Updated Daily
+ +
+

+
+United States Flag197 +  +
+ +
    +
  • %
  • +
+
+
+
+
+ +
+
+
+ +
+
+

+Alexa Traffic Ranks
+The global and country traffic ranks show how popular a site is relative to other sites. +

+ +

+Unique Visitors and Pageviews
+The number of people who visit this site and the number of pages they view. +Site owners who install the Alexa Certify Code on their website can choose +to display their Certified Metrics, such as Monthly Unique Visitors and +Pageviews, if they wish. Coming soon, estimated metrics will be displayed +for many sites if Certified Metrics are not available. +

+ +
+

How engaged are visitors to github.com?

+
+ + + +

Bounce Rate

+
+42.60% +3.00% +
+ +
    +
  • -3%
  • +
+
+
+
+
+ + + +

Daily Pageviews per Visitor

+
+5.96 +8.60% +
+ +
    +
  • 8.6%
  • +
+
+
+
+
+ + + +

Daily Time on Site

+
+5:39 +7.00% +
+ +
    +
  • 7%
  • +
+
+
+
+
+
+
+
+
+

+How engaged are visitors to this site?
+Engagement metrics help you understand how interested a site's visitors are with the site's content. The metrics are updated daily based on the trailing 3 months. +

+

+Bounce Rate (%)
+Percentage of visits to the site that consist of a single pageview. +

+

+Daily Pageviews per Visitor
+Estimated daily unique pageviews per visitor on the site. +

+

+Daily Time on Site
+Estimated daily time on site (mm:ss) per visitor to the site. +

+
+ +

Who visits github.com?

+
+
+

+Audience Demographics +

+

How similar is this site's audience to the general internet population?

+
+
+
+ +
+
+Gender +
+
+ +Male + + +
+ + +  + + + + +  + + + + + + + +Relative to the general internet population, Males are over-represented at this site.

Confidence: high
+ +
+
+
+
+
+
+ +Female + + +
+ + +  + + + + +  + + + + + + + +Relative to the general internet population, Females are under-represented at this site.

Confidence: high
+ +
+
+
+
+
+
+
+ +
+
+Education +
+
+ +No College + + +
+ + +  + + + + +  + + + + + + + +Relative to the general internet population, people who did not go to college are over-represented at this site.

Confidence: high
+ +
+
+
+
+
+
+ +Some College + + +
+ + +  + + + + +  + + + + + + + +Relative to the general internet population, people with some college education are over-represented at this site.

Confidence: high
+ +
+
+
+
+
+
+ +Graduate School + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among people who went to graduate school is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+ +College + + +
+ + +  + + + + +  + + + + + + + +Relative to the general internet population, people who went to college are under-represented at this site.

Confidence: high
+ +
+
+
+
+
+
+
+ +
+
+Browsing Location +
+
+ +Home + + +
+ + +  + + + + +  + + + + + + + +Relative to the general internet population, people browsing from home are under-represented at this site.

Confidence: high
+ +
+
+
+
+
+
+ +School + + +
+ + +  + + + + +  + + + + + + + +Relative to the general internet population, people browsing from school are greatly over-represented at this site.

Confidence: high
+ +
+
+
+
+
+
+ +Work + + +
+ + +  + + + + +  + + + + + + + +The audience for this site among people browsing from work is similar to the general internet population.

Confidence: high
+ +
+
+
+
+
+
+
+
+
+ +Upgrade to the Alexa Pro Insight Plan to view all demographics including age, income, ethnicity and children. + +Upgrade to View +
+
+ + +  + + +
+ +
+

Upgrade to view all demographics

+
+

Gain access to:

+
    +
  • - Age, income, children, ethnicity
      in additon to gender, education and browsing location.
  • +
  • - Comparisons of website demographics.
  • +
+
+
+

Upgrade to the Alexa Pro Insight Plan to
view all demographics.

+Upgrade +
+
+ +
+

Already have a subscription?

+

Login with your Alexa Account

+
+
+
+ + +
+
+ + + + + + +
+
+
+
+ + + + + +Forgot your password? + + +
+
+
+
+
+
+
+
+
+
+
+
+

+Audience Demographics
+The audience demographics data comes from voluntary demographics information submitted by people in our global traffic panel. The data is for the past 12 months, updated monthly. +Learn more +

+

The demographics data consists of:

+

+

+

+

+Audience Geography
+The audience geography data describes where visitors to this site over the past month are located, and how the site is ranked in popular countries. If a country is not listed, it is because Alexa does not have enough data for this site to rank/measure the site's popularity among that country's online population. These metrics are updated monthly. +

+
+
+
+

+Audience Geography +

+

Where are this site's visitors located?

+
+
+ +

Visitors by Country

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CountryPercent of VisitorsRank in Country
United States Flag  United States 21.3% 197
India Flag  India 12.4% 132
China Flag  China 4.6% 369
Brazil Flag  Brazil 3.6% 185
Japan Flag  Japan 3.5% 324
France Flag  France 3.4% 210
Russia Flag  Russia 3.4% 320
United Kingdom Flag  United Kingdom 2.9% 231
Germany Flag  Germany 2.7% 338
Spain Flag  Spain 2.5% 214
+
+
+
+ +

Where do github.com's visitors come from?

+ +
+

+Search Traffic +

+

What percentage of visits to this site come from a search engine?

+
+ + + +

Search Visits

+
+17.90% +5.00% +
+ +
    +
  • -5%
  • +
+
+
+
+
+ +
+

+Top Keywords from Search Engines +

+

Which search keywords send traffic to this site?

+
+ + + + + + + + + + + + + + + + + + + +
KeywordPercent of Search Traffic
  1.  github 2.57%
  2.  bootstrap 0.98%
  3.  cgminer 0.63%
  4.  laravel 0.51%
  5.  font awesome 0.42%
+
+ +Upgrade to the Alexa Pro Advanced Plan to view all keyword data. + +Upgrade to View +
+
+ + +  + + +
+ +
+

Upgrade to view all keyword data

+

Get more insight into your competitors' keyword strategy.

+
+

Gain access to:

+
    +
  • - Top organic keywords.
  • +
  • - Top paid keywords.
  • +
  • - Keyword competition.
  • +
  • - Keyword opportunities.
  • +
+
+
+

Upgrade to the Alexa Pro Advanced Plan to
view all keyword data.

+Upgrade +
+
+ +
+

Already have a subscription?

+

Login with your Alexa Account

+
+
+
+ + +
+
+ + + + + + +
+
+
+
+ + + + + +Forgot your password? + + +
+
+
+
+
+
+
+
+
+
+
+

+Search Traffic
+The percentage of traffic, both free and paid, that come to this site from a search engine over the past 3 months, updated daily. The change number shows the difference versus the previous 3 month period. +

+

+Top Keywords from Search Engines
+The table shows the top keywords that sent traffic to this site from major search engines over the past 6 months. The list is updated monthly. +

+

+Upstream Sites
+Upstream sites are sites that people visited just before they visited this site. Note that this list is not the same as referrals from upstream sites. There is not necessarily a link between the upstream site and this site. +

+
+
+
+ +
+

+Upstream Sites +

+

Which sites did people visit immediately before this site?

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SitePercent of Unique Visits
  1.  google.com 12.5%
  2.  stackoverflow.com 4.8%
  3.  github.io 2.9%
  4.  facebook.com 2.3%
  5.  google.co.in 1.9%
  6.  youtube.com 1.1%
  7.  githubusercontent.com 1.0%
  8.  twitter.com 0.9%
  9.  google.com.tr 0.8%
10.  google.com.vn 0.8%
+ +
+
+
+ +

Where do github.com's visitors go next?

+
+
+ +Upgrade to the Alexa Pro Insight Plan to view downstream sites. + +Upgrade to View +
+
+ + +  + + +
+ +
+

Upgrade to view downstream sites

+
+

Gain access to:

+
    +
  • - The top 10 sites visitors went to next.
  • +
+
+
+

Upgrade to the Alexa Pro Insight Plan to
view downstream sites.

+Upgrade +
+
+ +
+

Already have a subscription?

+

Login with your Alexa Account

+
+
+
+ + +
+
+ + + + + + +
+
+
+
+ + + + + +Forgot your password? + + +
+
+
+
+
+
+
+
+
+
+

+Downstream sites are sites that people visit immediately after visiting this site. Note this does not necessarily mean that people are directed to the downstream site by this site

+
+

What sites link to github.com?

+ +
+
Total Sites Linking In
+83,625 +
+
+

+
+ + + + + + + + + + + + + + + + + + + +
SitePage
1. youtube.com youtube.com/channel/UCZnwOxM6jQSfsftjm...
2. baidu.com anquan.baidu.com/bbs/thread-86532-1-1....
3. pconline.com.cn pcedu.pconline.com.cn/323/3234394.html
4. yahoo.com answers.yahoo.com/question/index?qid=2...
5. taobao.com taobao.com/go/chn/snsdkjs/guide.php?sp...
+
+ +Upgrade to the Alexa Pro Basic Plan to view all 83,625 sites linking in. + +Upgrade to View +
+
+ + +  + + +
+ +
+

Upgrade to view all 83,625 sites linking in

+

Subscribe to view all sites linking in

+
+

Gain access to:

+
    +
  • - Get the full list of sites linking in for any site.
  • +
  • - Benchmark your link-building efforts.
  • +
  • - See who is linking to your competitors.
  • +
+
+
+

Upgrade to the Alexa Pro Basic Plan to
view all 83,625 sites linking in.

+Upgrade +
+
+ +
+

Already have a subscription?

+

Login with your Alexa Account

+
+
+
+ + +
+
+ + + + + + +
+
+
+
+ + + + + +Forgot your password? + + +
+
+
+
+
+
+
+
+
+
+

The "Sites Linking In" count shows the number of sites that Alexa found that link to this site. +For more information please see this explanation of +how Alexa determines the number of sites linking in.

+

The complete list of sites linking to this site is available to Alexa Pro subscribers.

+
+

What sites are related to github.com?

+
+ +

Where do visitors go on github.com?

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SubdomainPercent of Visitors
github.com 95.35%
gist.github.com 9.14%
help.github.com 2.72%
windows.github.com 1.85%
enterprise.github.com 0.62%
mac.github.com 0.44%
developer.github.com 0.44%
status.github.com 0.29%
training.github.com 0.28%
shop.github.com 0.26%
octicons.github.com 0.24%
+
+
+

The table shows the top subdomains for this site ordered by the percentage of visitors +that visited the subdomain over a month. Note that the percentages can add up to more than +100% because a visitor can visit multiple subdomains during the month.

+

Updated Monthly.

+
+

How fast does github.com load?

Fast (1.176 Seconds), 68% of sites are slower.

+
+
+

The reported load time for a website is the median time it takes to load +pages from that site in a real users' web browsers.

+

Alexa takes the median of all the page load times we observe for a site +and then compares that to the same figure for all other sites. For example, +a site in the 98th percentile (Very Fast) has a median load time faster than +98% of all measured sites, while a site in the 2nd percentile (Very Slow) +loads more quickly than only 2% of all sites and is slower than 97% of all sites.

+

The load time of an individual page is how long it takes for the DOM - +the structure of the page - to be loaded. This time doesn't include the time +to load all images and stylesheets, for example.

+

The load time metric is updated monthly.

+
+

Where can I find more info about github.com?

+
+ + + +
+

GitHub

+
+
+

+
+ +

+Site Description +

+

GitHub is the best place to share code with friends, co-workers, classmates, and complete strangers. Over four million people use GitHub to build amazing things together.

+ +
+ +

+Contact +

+
GitHub
88 Colin P Kelly Jr St
San Francisco, CA 94107
USA
support [at] github.com

+
+
+ +
+

To edit your site's public information you need to verify ownership of your site.

+ +
+
+

Customize your site overview page with your logo, plus add links back to your site +and much more! An Enhanced Site Overview is just one of the features you get with +an Alexa PRO subscription.

+ +
+
+
+

+Site Description
+A short description of the site. +

+

+Contact
+How to contact the owner of the site. +

+
+ +
+
+ + + +
+
+
+
+
+
+

To edit your site's public information you need to sign in and verify ownership of your site.

+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/SEOstatsTest/_assert/Service/google-api-2014.json b/tests/SEOstatsTest/_assert/Service/google-api-2014.json new file mode 100644 index 00000000..823c9a12 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/google-api-2014.json @@ -0,0 +1 @@ +{"responseData": {"results":[{"GsearchResultClass":"GwebSearch","unescapedUrl":"https://github.com/","url":"https://github.com/","visibleUrl":"github.com","cacheUrl":"http://www.google.com/search?q\u003dcache:SZgkdCZ5k2sJ:github.com","title":"\u003cb\u003eGitHub\u003c/b\u003e · Build software better, together.","titleNoFormatting":"GitHub · Build software better, together.","content":"Online project hosting using Git. Includes source-code browser, in-line editing, \nwikis, and ticketing. Free for public open-source code. Commercial closed source\n ..."}],"cursor":{"resultCount":"7.200.000","pages":[{"start":"0","label":1},{"start":"1","label":2},{"start":"2","label":3},{"start":"3","label":4},{"start":"4","label":5},{"start":"5","label":6},{"start":"6","label":7},{"start":"7","label":8}],"estimatedResultCount":"7200000","currentPageIndex":0,"moreResultsUrl":"http://www.google.com/search?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\u003dde\u0026q\u003dgithub.com","searchResultTime":"0,24"}}, "responseDetails": null, "responseStatus": 200} diff --git a/tests/SEOstatsTest/_assert/Service/google-api-failed.json b/tests/SEOstatsTest/_assert/Service/google-api-failed.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/google-api-failed.json @@ -0,0 +1 @@ +{} diff --git a/tests/SEOstatsTest/_assert/Service/google-pagespeed-2014.json b/tests/SEOstatsTest/_assert/Service/google-pagespeed-2014.json new file mode 100644 index 00000000..4db51e59 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/google-pagespeed-2014.json @@ -0,0 +1,98 @@ +{ + "kind": "pagespeedonline#result", + "id": "/speed/pagespeed", + "responseCode": 200, + "title": "PageSpeed Home", + "score": 90, + "pageStats": { + "numberResources": 22, + "numberHosts": 7, + "totalRequestBytes": "2761", + "numberStaticResources": 16, + "htmlResponseBytes": "91981", + "cssResponseBytes": "37728", + "imageResponseBytes": "13909", + "javascriptResponseBytes": "247214", + "otherResponseBytes": "8804", + "numberJsResources": 6, + "numberCssResources": 2 + }, + "formattedResults": { + "locale": "en_US", + "ruleResults": { + "AvoidBadRequests": { + "localizedRuleName": "Avoid bad requests", + "ruleImpact": 0.0 + }, + "MinifyJavaScript": { + "localizedRuleName": "Minify JavaScript", + "ruleImpact": 0.1417, + "urlBlocks": [ + { + "header": { + "format": "Minifying the following JavaScript resources could reduce their size by $1 ($2% reduction).", + "args": [ + { + "type": "BYTES", + "value": "1.3KiB" + }, + { + "type": "INT_LITERAL", + "value": "0" + } + ] + }, + "urls": [ + { + "result": { + "format": "Minifying $1 could save $2 ($3% reduction).", + "args": [ + { + "type": "URL", + "value": "http://code.google.com/js/codesite_tail.pack.04102009.js" + }, + { + "type": "BYTES", + "value": "717B" + }, + { + "type": "INT_LITERAL", + "value": "1" + } + ] + } + }, + { + "result": { + "format": "Minifying $1 could save $2 ($3% reduction).", + "args": [ + { + "type": "URL", + "value": "http://www.gmodules.com/ig/proxy?url\u003dhttp%3A%2F%2Fjqueryjs.googlecode.com%2Ffiles%2Fjquery-1.2.6.min.js" + }, + { + "type": "BYTES", + "value": "258B" + }, + { + "type": "INT_LITERAL", + "value": "0" + } + ] + } + } + ] + } + ] + }, + "SpriteImages": { + "localizedRuleName": "Combine images into CSS sprites", + "ruleImpact": 0.0 + } + } + }, + "version": { + "major": 1, + "minor": 11 + } +} diff --git a/tests/SEOstatsTest/_assert/Service/google-pagespeed-failed.json b/tests/SEOstatsTest/_assert/Service/google-pagespeed-failed.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/google-pagespeed-failed.json @@ -0,0 +1 @@ +{} diff --git a/tests/SEOstatsTest/_assert/Service/google-search-2014-page-1.html b/tests/SEOstatsTest/_assert/Service/google-search-2014-page-1.html new file mode 100644 index 00000000..11aadf3c --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/google-search-2014-page-1.html @@ -0,0 +1,245 @@ + +github.com - Google-Suche
Wenn Sie ein Bildschirmleseprogramm verwenden, klicken Sie hier zum Deaktivieren von Google Instant.
Ungefähr 141.000.000 Ergebnisse (0,19 Sekunden) 
diff --git a/tests/SEOstatsTest/_assert/Service/google-search-2014-page-2.html b/tests/SEOstatsTest/_assert/Service/google-search-2014-page-2.html new file mode 100644 index 00000000..11aadf3c --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/google-search-2014-page-2.html @@ -0,0 +1,245 @@ + +github.com - Google-Suche
Wenn Sie ein Bildschirmleseprogramm verwenden, klicken Sie hier zum Deaktivieren von Google Instant.
Ungefähr 141.000.000 Ergebnisse (0,19 Sekunden) 
diff --git a/tests/SEOstatsTest/_assert/Service/google-search-failed.html b/tests/SEOstatsTest/_assert/Service/google-search-failed.html new file mode 100644 index 00000000..e69de29b diff --git a/tests/SEOstatsTest/_assert/Service/ose-siteinfo-2014.html b/tests/SEOstatsTest/_assert/Service/ose-siteinfo-2014.html new file mode 100644 index 00000000..0744d917 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/ose-siteinfo-2014.html @@ -0,0 +1,1616 @@ + + + + + Open Site Explorer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + +
+ +Open Site Explorer - Link Popularity Checker + Backlink Analysis Tool + +
+
+ + + +
+
+
+
+
+
New. You can now filter your Top Pages by HTTP Status code. Give it a try!
+
+ + +
+ + + +
+
+ Show + + + links + +
Link Types:
  • Link equity: Juice-passing links, including followed links and 301 (permanent) redirects.
  • No link equity: Links that do not pass link juice.
  • Only follow: Links that have not been defined as nofollow links.
  • Only rel=nofollow: Links with the rel=nofollow attribute.
  • Only 301: 301 (permanent) redirect links that pass link juice.
+ from + + pages + +
Link Sources:
  • Internal Pages Only: Links from pages on the domain
  • External Pages Only: Links from pages from external sites
+ + to + + +
Link Targets:
  • Subdomain: Links to pages that live on the subdomain.
  • Root Domain: Links to pages on the root domain *.domain.com. This includes pages on subdomains (ex. blog.seomoz.org or www.seomoz.org). Selecting Root Domain will expand your search beyond the initial page you entered.
+ + + and + + + + +
+
+ + +
+ Request CSV +
+ We can email you a CSV containing the info shown below, but you must + log in or register for an account. +
+
+ + + 1 - 50 inbound links from 9,018 domains + +
Although we do not currently display a count of total links for this filter set, you can still view all the links by paginating or downloading a CSV (up to 25 links per domain).
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Title and URL of Linking Page + Link Anchor Text +
The visible, clickable text in a hyperlink.
+
+ Page Authority +
Predicts this page's ranking potential in the search engines based on an algorithmic combination of all link metrics.
+
+ Domain Authority +
Predicts this domain's ranking potential in the search engines based on an algorithmic combination of all link metrics.
+
+
+ + Ruby on Rails + + +
(img alt)
GitHub
9290
+
+ (nofollow) + + Nginx Community + + +
GitHub8892
+
+ + Julie Ann Horvath Describes Sexism And Intimidation Behind Her GitHub Exit | TechCrunch + + +
http://github.com8696
+
+ (nofollow) + + Wiki Engines + + +
http://github.com8290
+
+ (nofollow) + + Free Social Media Icon Set | Elegant Themes Blog + + +
http://github.com/8294
+
+

As a Moz Pro subscriber you get:

+ +
    +
  • Unlimited Open Site Explorer reports
  • +
  • Valuable metrics for up to 10,000 links
  • +
  • Other SEO and inbound marketing tools
  • +
+
+
+
+ + After Editorially: The Search For Alternative Collaborative Online Writing Tools | Smashing Magazine + + +
+
+ + [No Data] + + +
+
+ (nofollow) + + [No Data] + + +
+
+ + HIV Testing Sites & Care Services Locator + + +
+
+ + [No Data] + + +
+
+ + Robin Good's Latest News + + +
+
+ + Zend PHP Webinars - Zend.com + + +
+
+ + Top Sites: The 500 Most Important Websites on the Internet - Moz + + +
+
+ + [No Data] + + +
+
+ + [No Data] + + +
+
+ + TCPDUMP/LIBPCAP public repository + + +
+
+ + [No Data] + + +
+
+ + The Silicon Valley iOS Developers' Meetup (Palo Alto, CA) - Meetup + + +
+
+ + [No Data] + + +
+
+ + Apache Maven Changes Plugin - Introduction + + +
+
+ + [No Data] + + +
+
+ + [No Data] + + +
+
+ + Report: Social network demographics in 2012 + + +
+
+ + [No Data] + + +
+
+ + David Heinemeier Hansson (DHH) + + +
+
+ + [No Data] + + +
+
+ + Google Cloud Platform Blog + + +
+
+ + GitHub For Beginners: Don't Get Scared, Get Started – ReadWrite + + +
+
+ + Techmeme Leaderboard + + +
+
+ + [No Data] + + +
+
+ + GitHub�Ȥ� - �ϤƤʥ������ + + +
+
+ + [No Data] + + +
+
+ + [No Data] + + +
+
+ + [No Data] + + +
+
+ + [No Data] + + +
+
+ + Introducing: Project Open Data | The White House + + +
+
+ + HIV Testing Sites & Care Services Locator + + +
+
+ + 7 Version Control Systems Reviewed | Smashing Magazine + + +
+
+ + Inside GitHub's Super-Lean Management Strategy--And How It Drives Innovation ⚙ Co.Labs ⚙ code + community + + +
+
+ + Julie Horvath “Satisfied” With GitHub Transparency | TechCrunch + + +
+
+ + 37signals Podcast + + +
+
+ (nofollow) + + [No Data] + + +
+
+ + [No Data] + + +
+
+ + [No Data] + + +
+
+ + Open Source Hardware Association + + +
+
+ + Thank you all - Free Web Analytics Software + + +
+
+ + What Exactly Is GitHub Anyway? | TechCrunch + + +
+
+ + [No Data] + + +
+
+ + [No Data] + + +
+
+ + [No Data] + + +
+
+ +
+ +
+ + +
+ + + +
+
+ +

+ Get more insight with Moz Pro +

+

+ Run unlimited reports and analyze your traffic and link metrics over time. +

+ Take a free 30-day trial of Moz Pro +
+ +
+ +

+ Moz Local New +

+

+ Correct, consistent business listings across the web—for all your + locations—at the click of a button. +

+ Learn More +
+
+ + + +
+
+ + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + diff --git a/tests/SEOstatsTest/_assert/Service/ose-siteinfo-failed.html b/tests/SEOstatsTest/_assert/Service/ose-siteinfo-failed.html new file mode 100644 index 00000000..e69de29b diff --git a/tests/SEOstatsTest/_assert/Service/sistrix-2013.html b/tests/SEOstatsTest/_assert/Service/sistrix-2013.html new file mode 100644 index 00000000..a57561a3 --- /dev/null +++ b/tests/SEOstatsTest/_assert/Service/sistrix-2013.html @@ -0,0 +1,11 @@ + + +
+

h2 foo bar

+

foobar

+

h3 foo1

+

h3 foo2

+

h3 foo3

+
+ + diff --git a/tests/SEOstatsTest/_assert/Service/sistrix-failed.html b/tests/SEOstatsTest/_assert/Service/sistrix-failed.html new file mode 100644 index 00000000..e69de29b diff --git a/tests/SEOstatsTest/_assert/buffer.txt b/tests/SEOstatsTest/_assert/buffer.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/SEOstatsTest/_assert/no_access.txt b/tests/SEOstatsTest/_assert/no_access.txt new file mode 100644 index 00000000..42f4d8cc --- /dev/null +++ b/tests/SEOstatsTest/_assert/no_access.txt @@ -0,0 +1,1088 @@ + + + + + + + + + + + + + SEOstats/README.md at master · eyecatchup/SEOstats · GitHub + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Skip to content +
+ + + + + + + +
+
+ + + + + + + +
+ + +
+ +
+ + + +
+ + + +
+ + This repository + + + +
+ + + + + + + + +
+
+ +
+
+ + + +
+
+
+ +
+
+
+ + + + +

+ public + + /SEOstats + + + + + +

+
+
+ +
+
+
+ + + + +
+ + + + +
+

HTTPS clone URL

+
+ + + + +
+
+ + + +
+

Subversion checkout URL

+
+ + + + +
+
+ + +

You can clone with + HTTPS + or Subversion. + + + +

+ + + + + + Download ZIP + +
+
+ +
+ + + + + + + + + +
+ + +
+ + + branch: + master + + + +
+ +
+ + + + +
+ + +
+ + +
+ Stephan Schmitz + + + + +
+

2 contributors

+ Stephan Schmitz + Francis Besset + + +
+ +
+ +
+
+
+
+ + file + + 459 lines (346 sloc) + + 15.726 kb +
+ +
+ +
+

Flattr SEOstats

+ +

+SEOstats: SEO metrics library for PHP

+ +

SEOstats is a powerful open source PHP library to request a bunch of SEO relevant metrics such as detailed backlink analyses, keyword and traffic statistics, website trends, page authority, the Google Pagerank, the Alexa Trafficrank and much more.

+ +

SEOstats offers over 50 different methods and gathers data from Alexa, Google, Mozscape (by Moz - f.k.a. Seomoz), SEMRush, Open-Site-Explorer, Sistrix, Facebook, Twitter & many more.

+ +

+Dependencies

+ +

SEOstats requires PHP version 5.3 or greater and the PHP5-CURL and PHP5-JSON extensions.

+ +

+Installation

+ +

The recommended way to install SEOstats is through composer. +To install SEOstats, just create the following composer.json file

+ +
{
+    "require": {
+        "seostats/seostats": "dev-master"
+    }
+}
+
+ +

and run the php composer.phar install (Windows: composer install) command in path of the composer.json.

+ +

+Step-by-step example:

+ +

If you haven't installed composer yet, here's the easiest way to do so:

+ +
# Download the composer installer and execute it with PHP:
+user@host:~/> curl -sS https://getcomposer.org/installer | php
+
+# Copy composer.phar to where your local executables live:
+user@host:~/> mv /path/given/by/composer-installer/composer.phar /usr/local/bin/composer.phar
+
+# Alternatively: For ease of use, you can add an alias to your bash profile:
+# (Note, you need to re-login your terminal for the change to take effect.)
+user@host:~/> echo 'alias composer="php /usr/local/bin/composer.phar"' >> ~/.profile
+
+ +

If you have installed composer, follow these steps to install SEOstats:

+ +
# Create a new directory and cd into it:
+user@host:~/> mkdir /path/to/seostats && cd /path/to/seostats
+
+# Create the composer.json for SEOstats:
+user@host:/path/to/seostats> echo '{"require":{"seostats/seostats":"dev-master"}}' > composer.json
+
+# Run the install command:
+user@host:/path/to/seostats> composer install
+Loading composer repositories with package information
+Installing dependencies (including require-dev)
+  - Installing seostats/seostats (dev-master 4c192e4)
+    Cloning 4c192e43256c95741cf85d23ea2a0d59a77b7a9a
+
+Writing lock file
+Generating autoload files
+
+# You're done. For a quick start, you can now 
+# copy the example files to the install directory:
+user@host:/path/to/seostats> cp ./vendor/seostats/seostats/example/*.php  ./
+
+# Your SEOstats install directory should look like this now:
+user@host:/path/to/seostats> ls -1
+composer.json
+composer.lock
+get-alexa-graphs.php
+get-alexa-metrics.php
+get-google-pagerank.php
+get-google-pagespeed-analysis.php
+get-google-serps.php
+get-opensiteexplorer-metrics.php
+get-semrush-graphs.php
+get-semrush-metrics.php
+get-sistrix-visibilityindex.php
+get-social-metrics.php
+vendor
+
+ +

+Use SEOstats without composer

+ +

If composer is no option for you, you can still just download the SEOstats.zip file of the current master branch (version 2.5.2) and extract it. However, currently there is an issues with autoloading and you need to follow the instructions in the comments in the example files in order to use SEOstats (or download zip for the development version of SEOstats (2.5.3) here).

+ +

+Usage

+ +

+TOC

+ +

+Configuration

+ +

There're two configuration files to note:

+ +
    +
  1. `./SEOstats/Config/ApiKeys.php`
    Client API Keys (currently required for Mozscape and Google's Pagespeed Service only). +
  2. +
  3. `./SEOstats/Config/DefaultSettings.php`
    Some default settings for querying data (mainly locale related stuff). +
  4. +

+Brief Example of Use

+ +

To use the SEOstats methods, you must include one of the Autoloader classes first (For composer installs: ./vendor/autoload.php; for zip download: ./SEOstats/bootstrap.php).

+ +

Now, you can create a new SEOstats instance an bind any URL to the instance for further use with any child class.

+ +
<?php
+// Depending on how you installed SEOstats
+#require_once __DIR__ . DIRECTORY_SEPARATOR . 'SEOstats' . DIRECTORY_SEPARATOR . 'bootstrap.php';
+require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
+
+use \SEOstats\Services as SEOstats;
+
+try {
+  $url = 'http://www.google.com/';
+
+  // Create a new SEOstats instance.
+  $seostats = new \SEOstats\SEOstats;
+
+  // Bind the URL to the current SEOstats instance.
+  if ($seostats->setUrl($url)) {
+
+    echo SEOstats\Alexa::getGlobalRank();
+    echo SEOstats\Google::getPageRank();
+  }
+}
+catch (SEOstatsException $e) {
+  die($e->getMessage());
+}
+
+ +

Alternatively, you can call all methods statically passing the URL to the methods directly.

+ +
<?php
+// Depending on how you installed SEOstats
+#require_once __DIR__ . DIRECTORY_SEPARATOR . 'SEOstats' . DIRECTORY_SEPARATOR . 'bootstrap.php';
+require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
+
+try {
+  $url = 'http://www.google.com/';
+
+  // Get the Google Toolbar Pagerank for the given URL.
+  echo \SEOstats\Services\Google::getPageRank($url);
+}
+catch (SEOstatsException $e) {
+  die($e->getMessage());
+}
+
+ +

More detailed examples can be found in the ./example directory.

+ +

+SEOstats Alexa Methods

+ +

+Alexa Traffic Metrics

+ +
<?php
+  // Returns the global Alexa Traffic Rank (last 3 months).
+  print Alexa::getGlobalRank();
+
+  // Returns the global Traffic Rank for the last month.
+  print Alexa::getMonthlyRank();
+
+  // Returns the global Traffic Rank for the last week.
+  print Alexa::getWeeklyRank();
+
+  // Returns the global Traffic Rank for yesterday.
+  print Alexa::getDailyRank();
+
+  // Returns the country-specific Alexa Traffic Rank.
+  print_r( Alexa::getCountryRank() );
+
+  // Returns Alexa's backlink count for the given domain.
+  print Alexa::getBacklinkCount();
+
+  // Returns Alexa's page load time info for the given domain.
+  print Alexa::getPageLoadTime();
+
+ +

+Alexa Traffic Graphs

+ +
<?php
+  // Returns HTML code for the 'daily traffic trend'-graph.
+  print Alexa::getTrafficGraph(1);
+
+  // Returns HTML code for the 'daily pageviews (percent)'-graph.
+  print Alexa::getTrafficGraph(2);
+
+  // Returns HTML code for the 'daily pageviews per user'-graph.
+  print Alexa::getTrafficGraph(3);
+
+  // Returns HTML code for the 'time on site (in minutes)'-graph.
+  print Alexa::getTrafficGraph(4);
+
+  // Returns HTML code for the 'bounce rate (percent)'-graph.
+  print Alexa::getTrafficGraph(5);
+
+  // Returns HTML code for the 'search visits'-graph, using specific graph dimensions of 320*240 px.
+  print Alexa::getTrafficGraph(6, 0, 320, 240);
+
+ +

+SEOstats Google Methods

+ +

+Google Toolbar PageRank

+ +
<?php
+  //  Returns the Google PageRank for the given URL.
+  print Google::getPageRank();
+
+ +

+Google Pagespeed Service

+ +
<?php
+  // Returns the Google Pagespeed analysis' metrics for the given URL.
+  print_r( Google::getPagespeedAnalysis() );
+
+  // Returns the Google Pagespeed analysis' total score.
+  print Google::getPagespeedScore();
+
+ +

+Google Websearch Index

+ +
<?php
+  // Returns the total amount of results for a Google site-search for the object URL.
+  print Google::getSiteindexTotal();
+
+  // Returns the total amount of results for a Google link-search for the object URL.
+  print Google::getBacklinksTotal();
+
+  // Returns the total amount of results for a Google search for 'keyword'.
+  print Google::getSearchResultsTotal('keyword');
+
+ +

+Google SERP Details

+ +
<?php
+  // Returns an array of URLs and titles for the first 100 results for a Google web search for 'keyword'.
+  print_r ( Google::getSerps('keyword') );
+
+  // Returns an array of URLs and titles for the first 200 results for a Google site-search for $url.
+  print_r ( Google::getSerps("site:$url", 200) );
+
+  // Returns an array of URLs, titles and position in SERPS for occurrences of $url
+  // within the first 1000 results for a Google web search for 'keyword'.
+  print_r ( Google::getSerps('keyword', 1000, $url) );
+
+ +

+SEOstats Mozscape Methods

+ +
<?php
+  // The normalized 10-point MozRank score of the URL. 
+  print Mozscape::getMozRank();
+
+  // The raw MozRank score of the URL.
+  print Mozscape::getMozRankRaw();
+
+  // The number of links (equity or nonequity or not, internal or external) to the URL.
+  print Mozscape::getLinkCount();
+
+  // The number of external equity links to the URL (http://apiwiki.moz.com/glossary#equity).
+  print Mozscape::getEquityLinkCount();
+
+  // A normalized 100-point score representing the likelihood
+  // of the URL to rank well in search engine results.  
+  print Mozscape::getPageAuthority();
+
+  // A normalized 100-point score representing the likelihood
+  // of the root domain of the URL to rank well in search engine results.
+  print Mozscape::getDomainAuthority();
+
+ +

+SEOstats Open Site Explorer (by MOZ) Methods

+ +
<?php
+  // Returns several metrics from Open Site Explorer (by MOZ)
+  $ose = OpenSiteExplorer::getPageMetrics();
+
+  // MOZ Domain-Authority Rank - Predicts this domain's ranking potential in the search engines 
+  // based on an algorithmic combination of all link metrics.
+  print "Domain-Authority:         " .
+        $ose->domainAuthority->result . ' (' .      // Int - e.g 42
+        $ose->domainAuthority->unit   . ') - ' .    // String - "/100"
+        $ose->domainAuthority->descr  . PHP_EOL;    // String - Result value description
+
+  // MOZ Page-Authority Rank - Predicts this page's ranking potential in the search engines 
+  // based on an algorithmic combination of all link metrics.
+  print "Page-Authority:           " .
+        $ose->pageAuthority->result . ' (' .        // Int - e.g 48
+        $ose->pageAuthority->unit   . ') - ' .      // String - "/100"
+        $ose->pageAuthority->descr  . PHP_EOL;      // String - Result value description
+
+  // Just-Discovered Inbound Links - Number of links to this page found over the past %n days, 
+  // indexed within an hour of being shared on Twitter.
+  print "Just-Discovered Links:    " .
+        $ose->justDiscovered->result . ' (' .       // Int - e.g 140
+        $ose->justDiscovered->unit   . ') - ' .     // String - e.g "32 days"
+        $ose->justDiscovered->descr  . PHP_EOL;     // String - Result value description
+
+  // Root-Domain Inbound Links - Number of unique root domains (e.g., *.example.com) 
+  // containing at least one linking page to this URL.
+  print "Linking Root Domains:     " .
+        $ose->linkingRootDomains->result . ' (' .   // Int - e.g 210
+        $ose->linkingRootDomains->unit   . ') - ' . // String - "Root Domains"
+        $ose->linkingRootDomains->descr  . PHP_EOL; // String - Result value description
+
+  // Total Links - All links to this page including internal, external, followed, and nofollowed.
+  print "Total Links:              " .
+        $ose->totalLinks->result . ' (' .           // Int - e.g 31571
+        $ose->totalLinks->unit   . ') - ' .         // String - "Total Links"
+        $ose->totalLinks->descr  . PHP_EOL;         // String - Result value description
+
+ +

+SEOstats SEMRush Methods

+ +

+SEMRush Domain Reports

+ +
<?php
+  // Returns an array containing the SEMRush main report (includes DomainRank, Traffic- & Ads-Data)
+  print_r ( SEMRush::getDomainRank() );
+
+  // Returns an array containing the domain rank history.
+  print_r ( SEMRush::getDomainRankHistory() );
+
+  // Returns an array containing data for competeing (auto-detected) websites.
+  print_r ( SEMRush::getCompetitors() );
+
+  // Returns an array containing data about organic search engine traffic, using explicitly SEMRush's german database.
+  print_r ( SEMRush::getOrganicKeywords(0, 'de') );
+
+ +

+SEMRush Graphs

+ +
<?php
+  // Returns HTML code for the 'search engine traffic'-graph.
+  print SEMRush::getDomainGraph(1);
+
+  // Returns HTML code for the 'search engine traffic price'-graph.
+  print SEMRush::getDomainGraph(2);
+
+  // Returns HTML code for the 'number of adwords ads'-graph, using explicitly SEMRush's german database.
+  print SEMRush::getDomainGraph(3, 0, 'de');
+
+  // Returns HTML code for the 'adwords traffic'-graph, using explicitly SEMRush's german database and
+  // specific graph dimensions of 320*240 px.
+  print SEMRush::getDomainGraph(4, 0, 'de', 320, 240);
+
+  // Returns HTML code for the 'adwords traffic price '-graph, using explicitly SEMRush's german database,
+  // specific graph dimensions of 320*240 px and specific graph colors (black lines and red dots for data points).
+  print SEMRush::getDomainGraph(5, 0, 'de', 320, 240, '000000', 'ff0000');
+
+ +

+SEOstats Sistrix Methods

+ +

+Sistrix Visibility Index

+ +
<?php
+  // Returns the Sistrix visibility index
+  // @link http://www.sistrix.com/blog/870-sistrix-visibilityindex.html
+  print Sistrix::getVisibilityIndex();
+
+ +

+SEOstats Social Media Methods

+ +

+Google+ PlusOnes

+ +
<?php
+  // Returns integer PlusOne count
+  print Social::getGooglePlusShares();
+
+ +

+Facebook Interactions

+ +
<?php
+  // Returns an array of total counts for overall Facebook interactions count, shares, likes, comments and clicks.
+  print_r ( Social::getFacebookShares() );
+
+ +

+Twitter Mentions

+ +
<?php
+  // Returns integer tweet count for URL mentions
+  print Social::getTwitterShares();
+
+ +

+Other Shares

+ +
<?php
+  // Returns the total count of URL shares via Delicious
+  print Social::getDeliciousShares();
+
+  // Returns array of top ten delicious tags for a URL
+  print_r ( Social::getDeliciousTopTags() );
+
+  // Returns the total count of URL shares via Digg
+  print Social::getDiggShares();
+
+  // Returns the total count of URL shares via LinkedIn
+  print Social::getLinkedInShares();
+
+  // Returns shares, comments, clicks and reach for the given URL via Xing
+  print_r( Social::getXingShares() );
+
+  // Returns the total count of URL shares via Pinterest
+  print Social::getPinterestShares();
+
+  // Returns the total count of URL shares via StumbleUpon
+  print Social::getStumbleUponShares();
+
+  // Returns the total count of URL shares via VKontakte
+  print Social::getVKontakteShares();
+
+ +

+License

+ +

(c) 2010 - 2014, Stephan Schmitz eyecatchup@gmail.com
+License: MIT, http://eyecatchup.mit-license.org
+URL: https://github.com/eyecatchup/SEOstats

+
+ +
+
+ + + + +
+ +
+ +
+
+ + +
+ +
+ +
+ + +
+
+
+ +
+
+ +
+ + + +
+ + + Something went wrong with that request. Please try again. +
+ + + + + + + + + + diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 00000000..4c6d3af4 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,20 @@ +addClassMap($classMap1); + +error_reporting(E_ALL); + +$configApiFilePath = dirname(__DIR__) . '/SEOstats/Config/ApiKeys.php'; + +$configApi = file_get_contents($configApiFilePath); + +$configApi = preg_replace("#(\s+const MOZSCAPE_ACCESS_ID\s+=) \'\';#", "\$1 'MOZSCAPE_ACCESS_ID';", $configApi); +$configApi = preg_replace("#(\s+const MOZSCAPE_SECRET_KEY\s+=) \'\';#", "\$1 'MOZSCAPE_SECRET_KEY';", $configApi); +$configApi = preg_replace("#(\s+const GOOGLE_SIMPLE_API_ACCESS_KEY\s+=) \'\';#", "\$1 'GOOGLE_SIMPLE_API_ACCESS_KEY';", $configApi); + + +file_put_contents($configApiFilePath, $configApi); diff --git a/tests/phpunit.xml b/tests/phpunit.xml new file mode 100644 index 00000000..953fcfdd --- /dev/null +++ b/tests/phpunit.xml @@ -0,0 +1,43 @@ + + + + + + + ./SEOstatsTest + + + + + + performance + + + + + + ../SEOstats + + ../SEOstats/Services/3rdparty + + + + + + + + + + +