<?php

/**********************************************************************
GooglePR -- Calculates the Google PageRank of a specified URL
Authors : Emre Odabas (emre [at] golge [dot] net)
Version : 2.0

Description
What is Google PageRank?

PageRank is a family of algorithms for assigning numerical weightings
to hyperlinked documents (or web pages) indexed by a search engine.
Its properties are much discussed by search engine optimization (SEO)
experts. The PageRank system is used by the popular search engine
Google to help determine a page's relevance or importance.

As Google puts it:

> PageRank relies on the uniquely democratic nature of the web by
> using its vast link structure as an indicator of an individual
> page's value. Google interprets a link from page A to page B as
> a vote, by page A, for page B. But Google looks at more than the
> sheer volume of votes, or links a page receives; it also analyzes
> the page that casts the vote. Votes cast by pages that are
> themselves "important" weigh more heavily and help to make other
> pages "important."

For more info:
http://www.google.com/corporate/tech.html
http://en.wikipedia.org/wiki/PageRank
http://www.google.com/webmasters/4.html

This class will calculate and return the Google PageRank of the
specified input URL as integer. Class was build based on Raistlin
Majere's google_pagerank function

Change Log:

  2008-01-24  * Hash calculation functions updated because of
                miscalculation based on php versions. 
                (algorithm updated based on a anonymous source
                code which supposed to be found at
                http://pagerank.gamesaga.net but not exists 
                any more.)
  2005-12-07  * Small bug removed (dies when caching disabled)
  2005-11-24  * Added user-agent support
              * Class selects random google hostnames in
                order to prevent abuse. (You may define extra
                google hostnames)
              * Class now first tries cURL, fsockopen() and
                file_get_contents() to connect google servers.
              * Added caching option to class. Results now can be
                cached to flat files in order to prevent abuse and
                increase performance.
              * Cache files are stored in seperate directories for
                performance issues.

  2005-11-04  * Initial version released


Ex:
$gpr = new GooglePR();
//$gpr->debug=true; //Uncomment this line to debug query process
echo $gpr->GetPR("http://www.progen.com.tr");

//Uncomment following line to view debug results
//echo "<pre>";print_r($gpr->debugResult);echo "</pre>";

**********************************************************************/
Class GooglePR {

	//Public vars
	var $googleDomains = Array(
		"toolbarqueries.google.com",
		"www.google.com",
		"toolbarqueries.google.com.tr",
		"www.google.com.tr",
		"toolbarqueries.google.de",
		"www.google.de", 
		"64.233.187.99", 
		"72.14.207.99");
	
	var $debugResult = Array();
	var $userAgent = "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.2.1) Gecko/20021204";
	var $cacheDir = "/tmp";
	var $maxCacheAge = 86400; // = 24h (yes, in seconds)
	var $useCache = false;
	var $debug = false;

	//Private vars
	var $PageRank = -1;
	var $cacheExpired = false;


	function GetPR($url,$forceNoCache = false) {
		$total_exec_start = $this->microtimeFloat();
		$result=array("",-1);

		if (($url.""!="")&&($url.""!="http://")) {

			$this->debugRes("url", $url);

			$this->cacheDir .= (substr($this->cacheDir,-1) != "/")? "/":"";

			// check for protocol
			$url_ = ((substr(strtolower($url),0,7)!="http://")? "http://".$url:$url);
			$host = $this->googleDomains[mt_rand(0,count($this->googleDomains)-1)];
			$target = "/search";
			$querystring = sprintf("client=navclient-auto&ch=%s&features=Rank&q=%s",
				$this->CheckHash($this->HashURL($url_)),urlencode("info:".$url_));
			$contents="";

			$this->debugRes("host", $host);
			$this->debugRes("query_string", $querystring);
			$this->debugRes("user_agent", $this->userAgent);

			$query_exec_start = $this->microtimeFloat();

			if ($forceNoCache == true) {
				$this->debugRes("force_no_cache", "true");
			} elseif ($contents = $this->readCacheResult($url)) {
				$this->debugRes("read_from_cache", "true");
			} else {
				$this->cacheExpired = true;
			}


			// let's get ranking
			if (strlen(trim($contents)) == 0)
			if (@function_exists("curl_init")) {

				// allways use curl if available for performance issues
				$ch = curl_init();
				curl_setopt($ch, CURLOPT_URL, "http://".$host.$target."?".$querystring);
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
				curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
				curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent);
				if (!($contents = trim(@curl_exec($ch)))) {
					$this->debugRes("error","curl_exec failed");
				}
				curl_close ($ch);
				$this->debugRes("method", "curl");

			} else {
				$this->debugRes("error","curl not installed, trying to use fsockopen");
				// use fsockopen as secondary method, to submit user agent
				if ($socket  = @fsockopen($host, "80", $errno, $errstr, 30)) {

					$request  = "GET $target?$querystring HTTP/1.0\r\n";
					$request .= "Host: $host\r\n";
					$request .= "User-Agent: ".$this->userAgent."\r\n";
					$request .= "Accept-Language: en-us, en;q=0.50\r\n";
					$request .= "Accept-Charset: ISO-8859-1, utf-8;q=0.66, *;q=0.66\r\n";
					$request .= "Accept: text/xml,application/xml,application/xhtml+xml,";
					$request .= "text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,";
					$request .= "image/jpeg,image/gif;q=0.2,text/css,*/*;q=0.1\r\n";
					$request .= "Connection: close\r\n";
					$request .= "Cache-Control: max-age=0\r\n\r\n";

					stream_set_timeout ( $socket,10);
					fwrite( $socket, $request );
					$ret = '';
					while (!feof($socket)) {
						$ret .= fread($socket,4096);
					}
					fclose($socket);
					$contents = trim(substr($ret,strpos($ret,"\r\n\r\n") + 4));
					$this->debugRes("method", "fsockopen");
				} else {
					$this->debugRes("error","fsockopen failed, trying file_get_contents");
					// this way could cause problems because the Browser Useragent is not set...
					if ($contents = trim(@file_get_contents("http://".$host.$target."?".$querystring))) {
						$this->debugRes("method", "file_get_contents");
					} else {
						$this->debugRes("error","file_get_contents failed");
					}
				}

			}

			if ($this->cacheExpired == true)
			$this->updateCacheResult($url,$contents);

			$this->debugRes("query_exec_time",$this->microtimeFloat() - $query_exec_start);

			$result[0]=$contents;
			// Rank_1:1:0 = 0
			// Rank_1:1:5 = 5
			// Rank_1:1:9 = 9
			// Rank_1:2:10 = 10 etc
			$p=explode(":",$contents);
			if (isset($p[2])) $result[1]=$p[2];
		}

		if($result[1] == -1) $result[1] = 0;
		$this->PageRank =(int)$result[1];
		$this->debugRes("total_exec_time", $this->microtimeFloat() - $total_exec_start);
		$this->debugRes("result", $result);
		return $this->PageRank;

	}


	function debugRes($what,$sowhat) {
		if($this->debug == true) {
			$debugbt = debug_backtrace();
			$what = trim($what);
			$sowhat = trim($sowhat) . " (Line : ".$debugbt[0]["line"].")";
			if ($what == "error") {
				$this->debugResult[$what][] = $sowhat;
			} else {
				$this->debugResult[$what] = $sowhat;
			}
		}
	}

	function microtimeFloat() {
		list($usec, $sec) = explode(" ", microtime());
		return ((float)$usec + (float)$sec);
	}


	function readCacheResult($url) {
		if ($this->useCache != true) {
			return false;
		}

		if (!is_dir($this->cacheDir)) {
			$this->debugRes("error","please create {$this->cacheDir}");
			return false;
		}

		$urlp = parse_url($url);
		$host_ = explode(".",$urlp["host"]);
		$path_ = (strlen($urlp["query"])>0)? urlencode($urlp["path"].$urlp["query"]):"default";

		$cache_file = $this->cacheDir;

		for ($i = count($host_)-1;$i>=0;$i--) {
			$cache_file .= $host_[$i]."/";
		}

		$cache_file .= $path_;
		$this->debugRes("cache_file", $cache_file);
		if (file_exists($cache_file)) {
			$mtime = filemtime($cache_file);
			if (time() - $mtime > $this->maxCacheAge) {
				$this->debugRes("cache", "expired");
				$this->cacheExpired = true;
				return false;
			} else {
				$this->cacheExpired = false;
				$this->debugRes("cache_age", time() - $mtime);
				return file_get_contents($cache_file);
			}
		}
		$this->debugRes("error","cache file not exists (reading)");
		return false;
	}

	function updateCacheResult($url,$content) {
		if ($this->useCache != true) {
			return false;
		}

		if (!is_dir($this->cacheDir)) {
			$this->debugRes("error","please create {$this->cacheDir}");
			return false;
		}

		$urlp = parse_url($url);
		$host_ = explode(".",$urlp["host"]);
		$path_ = (strlen($urlp["query"])>0)? urlencode($urlp["path"].$urlp["query"]):"default";

		$cache_file = $this->cacheDir;
		for ($i = count($host_)-1;$i>=0;$i--) {
			$cache_file .= $host_[$i]."/";
		}

		$cache_file .= $path_;

		if (!file_exists($cache_file)) {
			$this->debugRes("error","cache file not exists (writing)");
			$cache_file_tmp = substr($cache_file,strlen($this->cacheDir));
			$cache_file_tmp = explode("/",$cache_file_tmp);
			$cache_dir_ = $this->cacheDir;
			for ($i = 0;$i<count($cache_file_tmp)-1;$i++) {
				$cache_dir_ .= $cache_file_tmp[$i]."/";
				if (!file_exists($cache_dir_)) {
					if (!@mkdir($cache_dir_,0777)) {
						$this->debugRes("error","unable to create cache dir: $cache_dir_");
						//break;
					}
				}
			}
			if (!@touch($cache_file)) $this->debugRes("error","unable to create cache file");
			if (!@chmod($cache_file,0777)) $this->debugRes("error","unable to chmod cache file");
		}

		if (is_writable($cache_file)) {
			if (!$handle = fopen($cache_file, 'w')) {
				$this->debugRes("error", "unable to open $cache_file");
				return false;
			}
			if (fwrite($handle, $content) === FALSE) {
				$this->debugRes("error", "unable to write to $cache_file");
				return false;
			}
			fclose($handle);
			$this->debugRes("cached", date("Y-m-d H:i:s"));
			return true;
		}
		$this->debugRes("error", "$cache_file is not writable");
		return false;

	}

	//convert a string to a 32-bit integer
	function StrToNum($Str, $Check, $Magic) {
		$Int32Unit = 4294967296;  // 2^32
		$length = strlen($Str);
		for ($i = 0; $i < $length; $i++) {
			$Check *= $Magic; 	
			//If the float is beyond the boundaries of integer (usually +/- 2.15e+9 = 2^31), 
			//  the result of converting to integer is undefined
			//  refer to http://www.php.net/manual/en/language.types.integer.php
			if ($Check >= $Int32Unit) {
				$Check = ($Check - $Int32Unit * (int) ($Check / $Int32Unit));
				//if the check less than -2^31
				$Check = ($Check < -2147483648) ? ($Check + $Int32Unit) : $Check;
			}
			$Check += ord($Str{$i}); 
		}
		return $Check;
	}

	//genearate a hash for a url
	function HashURL($String) {
		$Check1 = $this->StrToNum($String, 0x1505, 0x21);
		$Check2 = $this->StrToNum($String, 0, 0x1003F);
		$Check1 >>= 2; 	
		$Check1 = (($Check1 >> 4) & 0x3FFFFC0 ) | ($Check1 & 0x3F);
		$Check1 = (($Check1 >> 4) & 0x3FFC00 ) | ($Check1 & 0x3FF);
		$Check1 = (($Check1 >> 4) & 0x3C000 ) | ($Check1 & 0x3FFF);	
		
		$T1 = (((($Check1 & 0x3C0) << 4) | ($Check1 & 0x3C)) <<2 ) | ($Check2 & 0xF0F );
		$T2 = (((($Check1 & 0xFFFFC000) << 4) | ($Check1 & 0x3C00)) << 0xA) | ($Check2 & 0xF0F0000 );
		
		return ($T1 | $T2);
	}
	
	//genearate a checksum for the hash string
	function CheckHash($Hashnum) {
		$CheckByte = 0;
		$Flag = 0;
		$HashStr = sprintf('%u', $Hashnum) ;
		$length = strlen($HashStr);
		
		for ($i = $length - 1;  $i >= 0;  $i --) {
			$Re = $HashStr{$i};
			if (1 === ($Flag % 2)) {			  
				$Re += $Re;	 
				$Re = (int)($Re / 10) + ($Re % 10);
			}
			$CheckByte += $Re;
			$Flag ++;	
		}
	
		$CheckByte %= 10;
		if (0 !== $CheckByte) {
			$CheckByte = 10 - $CheckByte;
			if (1 === ($Flag % 2) ) {
				if (1 === ($CheckByte % 2)) {
					$CheckByte += 9;
				}
				$CheckByte >>= 1;
			}
		}
		return '7'.$CheckByte.$HashStr;
	}
}

?>