<?php
##
## this file name is 'class.google.php'
##
## ping object
##
## [author]
##  - san2(at)linuxchannel.net
##
## [changes]
##  - 2014.12.09 : timeout tunning
##  - 2012.11.xx : some
##  - 2008.12.05 : add more
##  - 2008.12.01 : new build
##
## [references]
##  - http://kr.php.net/manual/en/function.socket-create.php
##  - http://ftp.linuxchannel.net/devel/php_download/
##
## [usage]
##
## [example]
##
class google
{
  
## googel network latency
  ##
  
public function &ping($host)
  {
    static 
$port 0;
    static 
$datasize 58// default 58 bytes + 8 = 64 bytes
    
static $timeout = array('sec'=>0,'usec'=>500000);

    
$ident = array(ord('J'), ord('C'));
    
$seq = array(rand(0,255), rand(0,255));

    
## 8 bytes of ICMP header data
    
$packet '';
    
$packet .= chr(8); // type = 8 : request
    
$packet .= chr(0); // code = 0
    
$packet .= chr(0); // checksum init
    
$packet .= chr(0); // checksum init
    
$packet .= chr($ident[0]); // identifier
    
$packet .= chr($ident[1]); // identifier
    
$packet .= chr($seq[0]); // seq
    
$packet .= chr($seq[1]); // seq

    
for($i=0$i<$datasize$i++) $packet .= chr(0);

    
$chk google::icmpchecksum($packet);

    
$packet[2] = $chk[0]; // checksum init
    
$packet[3] = $chk[1]; // checksum init

    
if(preg_match('/[a-z]/',$host)) $host gethostbyname($host); // to ip

    
if(!$sock = @socket_create(AF_INETSOCK_RAWgetprotobyname('icmp')))
    {
        return array(-
1socket_last_error());
    }

    @
socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,$timeout); // good idea for blocking mode
    
@socket_set_option($sock,SOL_SOCKET,SO_SNDTIMEO,$timeout);

    
$start microtime();
    @
socket_sendto($sock$packetstrlen($packet), 0$host$port);

    
$read   = array($sock);
    
$write  NULL;
    
$except NULL;
    
$select = @socket_select($read$write$except10);

    if(
$select === NULL)
    {
        @
socket_close($sock);
        return array(-
1,'Select Error');
    }
    else if(
$select === 0)
    {
        @
socket_close($sock);
        return array(-
1,'Timeout');
    }

    
$recv '';
    @
socket_recvfrom($sock$recv655350$host$port);
    
$end microtime();
    @
socket_close($sock);

    
$recv unpack('C*'$recv);

    
// ICMP proto = 1
    
if($recv[10] !== 1) return array(-1,"Not ICMP packet");

    
// ICMP response = 0
    
if($recv[21] !== 0) return array(-1,"Not ICMP response");
    if(
$ident[0] !== $recv[25] || $ident[1] !== $recv[26]) return array(-1,"Bad identification number");
    if(
$seq[0] !== $recv[27] || $seq[1] !== $recv[28]) return array(-1,"Bad sequence number");

    
$rtt google::getms($start$end);

    return array(
$rtt);
  }

  private function &
icmpchecksum($data)
  {
    
$bit unpack('n*'$data);
    
$sum array_sum($bit);

    if (
strlen($data) % 2)
    {
        
$temp unpack('C*'$data[strlen($data) - 1]);
        
$sum += $temp[1];
    }

    
$sum = ($sum >> 16) + ($sum 0xffff);
    
$sum += ($sum >> 16);

    return 
pack('n*', ~$sum);
  }

  
## google map loading server latency
  ## default non-blocking mode
  ##
  
public function &gethttp($_url$iporhost=''$rhskip=FALSE)
  {
    static 
$bsize 1048576// socket send/receive buffer size(Bytes)
    
static $timeout = array('sec'=>0,'usec'=>500000);

    list(
$host,$ip,$port,$uri) = google::gethostip($_url,$iporhost);

    if(!
$sock = @socket_create(AF_INETSOCK_STREAMSOL_TCP))
    {
        return array(-
1socket_last_error());
    }

    @
socket_set_option($sock,SOL_SOCKET,SO_SNDBUF,$bsize);
    @
socket_set_option($sock,SOL_SOCKET,SO_RCVBUF,$bsize);
    @
socket_set_option($sock,SOL_SOCKET,SO_RCVTIMEO,$timeout); // good idea for blocking mode
    
@socket_set_option($sock,SOL_SOCKET,SO_SNDTIMEO,$timeout);

    @
socket_set_nonblock($sock); // set to non-blocking mode

    
$req 'GET '.$uri.' HTTP/1.1'."\r\n".
        
'Host: '.$host."\r\n".
        
'User-Agent: PHP/google'."\r\n".
        
'Connection: close'."\r\n"// good idea for blocking mode
        
"\r\n"// enf of request header mark

    
$start microtime();
    
$code google::_connect($sock,$ip,$port,5);
    if(
$code != 1)
    {
        @
socket_close($sock);
        return array(-
1"can not connect to $ip:$port");
    }

    if(!@
socket_write($sock,$req))
    {
        @
socket_close($sock);
        return array(-
1"can not send request $uri");
    }

    
$header google::_getheader($sock,1024); // read header
    
$header google::_parse_header($header);

    if(!
$rhskip && $header['error'])
    {
        @
socket_close($sock);
        return array(-
1$header['status'].' '.$uri);
    }

    
//$total = $header['content-length']; // total size
    //$pg = $debug ? $total : 0; // debug mode with progress

    
$size google::_readsize($sock,$bsize); // check bandwidth mode
    //google::_readbyall($sock,$buf='',$bsize,$blocking,$pg);
    //$size = strlen($buf);

    
@socket_close($sock);
    
$end microtime();

    return array(
google::getms($start,$end));
  }

  public function 
gethostip($_url$iporhost='')
  {
    list(
$nhost,$port,$uri) = $c google::_parse_url($_url);

    if(
preg_match('/[a-z]/',$nhost)) $host $nhost;
    else 
$ip $nhost;

    if(
$iporhost)
    {
        if(
preg_match('/[a-z]/',$iporhost)) $host $iporhost;
        else 
$ip $iporhost;
    }

    if(!
$ip$ip gethostbyname($host); // to ip
    
else if(!$host$host $ip// not good

    
return array($host,$ip,$port,$uri);
  }

  
## connect to host:port
  ##
  ## [return]
  ##  1 or TRUE  : connected
  ##  0 or FALSE : can't connect
  ##  -1         : connetion time out
  ##
  
private function &_connect(&$sock$host$port$timeout=3)
  {
    
$_time time();
    while(
1)
    {
        @
socket_connect($sock,$host,$port);
        if(@
socket_last_error()==56 || @socket_last_error()==106) { return TRUE; break; }
        else if((
time()-$_time) >= $timeout) { return -1; break; }
    }

    return 
FALSE;
  }

  
## read header by socket_read()
  ## PHP_NORMAL_READ : 1
  ## PHP_BINARY_READ : 2
  ##
  
private function &__readheader(&$sock$bsize)
  {
    while(
$buf = @socket_read($sock,$bsize,PHP_NORMAL_READ))
    {
        
//if(!defined('_RESPONSE_END_')) define('_RESPONSE_END_',microtime());
        
if(!trim($obuf.$buf))
        {
            @
socket_read($sock,$bsize,PHP_NORMAL_READ); // seek 1 bytes
            
break; // end of header
        
}
        
$rbuf .= $buf;     $obuf $buf;
    }

    return 
$rbuf;
  }

  
## read HTTP all headers
  ##
  
private function &_getheader(&$sock$bsize=1024)
  {
    
$stream = array($sock);
    while(@
socket_select($stream,$write=NULL,$except=NULL,1,0) !== FALSE)
    {
        
//if(!in_array($sock,$stream)) break; // is bug ???
        
$rbuf google::__readheader($sock,$bsize);
        break; 
// require
    
}

    return 
$rbuf;
  }

  private function &
_parse_header($buf)
  {
    
$lines preg_split('/[\r\n]+/',trim($buf));
    
$size sizeof($lines);
    
$r['status'] = trim($lines[0]);

    if(!
preg_match(';200 OK;',$lines[0]))
    {
        
$r['error'] = TRUE;
        return 
$r;
    }

    for(
$i=1$i<$size$i++)
    {
        
$line trim($lines[$i]);
        list(
$k) = preg_split('/:\s+/',$line);
        
$r[strtolower($k)] = preg_replace(';^'.$k.':\s+;','',$line);
    }

    return 
$r;
  }

  
## only read size, this is a bandwidth test version
  ##
  
private function &_readsize(&$sock$bsize=1048576)
  {
    
$stream = array($sock);
    while(@
socket_select($stream,$write=NULL,$except=NULL,1,0) !== FALSE)
    {
        if(!
in_array($sock,$stream)) break;
        if(
$tsize strlen(@socket_read($sock,$bsize)))
        {
            
$rsize += $tsize;
        } else break; 
// good idea, EOF
    
}

    return 
$rsize;
  }

  private function &
getms($_start$_end)
  {
    
$end explode(' '$_end);
    
$start explode(' '$_start);

    return 
sprintf('%.3f',(($end[1]+$end[0])-($start[1]+$start[0])) * 1000);
  }

  private function &
_parse_url($_url)
  {
    
$url parse_url(preg_replace(';^(?:http://)*;i','http://',trim($_url)));

    
$host $url['host'];
    
$port $url['port'] ? $url['port'] : 80;
    
$uri $url['path'] ? $url['path'] : '/';

    if(
$url['query']) $uri .= '?'.$url['query']; // add san2@2008.12.01

    
return array($host,$port,$uri);
  }

  private function &
error($errstr)
  {
    return 
error_log($errstr,0);
  }
// end of class
?>