<?php
/**
 * LRG
 *
 * Linear-Recursive Grabber
 *
 * @descr        CLI Only
 * @package        LRG
 * @author        HSDN Team
 * @copyright    Copyright (c) 2010 Information Networks Ltd.
 * @link        http://www.hsdn.org
 * @since        Version 1.1
 */

// ------------------------------------------------------------------------

/**
 * ПРИМЕЧАНИЕ
 *
 * Настоящий программный код является неотъемлемым компонентом 
 * программного продукта Linear-Recursive Grabber, однако он может 
 * поставляться и с другими программными продуктами.
 */

// ------------------------------------------------------------------------

/**
 * Класс Http
 *
 * @category    Libraries
 * @author        HSDN Team
 */
class Http
{
    
/*
     * Режим отладки
     *
     * @access    private
     */
    
private $debug;

    
/*
     * Номер ошибки соединения
     *
     * @access    private
     */
    
public $errno NULL;

    
/*
     * Строка ошибки соединения
     *
     * @access    private
     */
    
public $errstr NULL;


    
/**
     * Конструктор
     *
     * @access    public
     * @param    bool
     * @return    void
     */
    
public function __construct($debug FALSE)
    {
        
$this->debug $debug;
    }

    
/**
     * Открытие соединения
     *
     * @access    public
     * @param    string
     * @param    int
     * @param    string
     * @return    bool
     */
    
public function open($host$port$type 'tcp')
    {
        
$hosts = @gethostbynamel($host);

        if (
is_array($hosts) AND reset($hosts) != $host)
        {
            foreach (
$hosts as $ip)
            {
                if (
$socket $this->open($ip$port$type))
                {
                    return 
$socket;
                }
            }
        }
        else
        {
            
$socket = @stream_socket_client($type.'://'.$host.':'.$port$this->errno$this->errstr5);

            if (
is_resource($socket))
            {
                return 
$socket;
            }
        }

        return 
FALSE;
    }

    
/**
     * Блокировка потока
     *
     * @access    public
     * @param    resource
     * @param    bool
     * @return    void
     */
    
public function set_blocking(&$socket$blocking)
    {
        if(!
is_resource($socket)) 
        {
            return 
FALSE;
        }

        
stream_set_blocking($socket$blocking);
    }

    
/**
     * Чтение HTTP контента
     *
     * @access    public
     * @param    resource
     * @return    bool
     */
    
public function get_content(&$socket)
    {
        if(!
is_resource($socket)) 
        {
            return 
FALSE;
        }

        
$head $this->read_head($socket);
        
$code $this->parse_status_code($head);
        
$headers $this->parse_headers($head);
        
$body $this->read($socket);

        return array
        (
            
'code' => $code,
            
'headers' => $headers,
            
'body' => $body
        
);
    }

    
/**
     * Чтение заголовков HTTP из потока
     *
     * @access    public
     * @param    resource
     * @return    string
     */
    
public function read_head(&$socket)
    {
        if(!
is_resource($socket)) 
        {
            return 
FALSE;
        }

        
$read '';

        while (
trim($line $this->read_line($socket)))
        {
            
$read .= $line ;
        }

        return 
$read;
    }

    
/**
     * Чтение тела HTTP из потока
     *
     * @access    public
     * @param    resource
     * @return    string
     */
    
public function read_body(&$socket)
    {
        if(!
is_resource($socket)) 
        {
            return 
FALSE;
        }

        
$this->read_head($socket);

        return 
$this->read($socket);
    }

    
/**
     * Чтение данных из потока
     *
     * @access    public
     * @param    resource
     * @return    bool
     */
    
public function read(&$socket
    { 
        if(!
is_resource($socket))  
        { 
            return 
FALSE
        } 

        
$read ''

        if (
$this->debug
        { 
            echo 
'BEGIN RECEIVING --> '
            
flush();
        } 

        
$d 0;

        while (
TRUE)  
        {
            
$read .= $s fread($socket1); 
            
$socet_status stream_get_meta_data($socket);

            if (
$socet_status['timed_out'] != FALSE OR $socet_status['eof'] == TRUE)
            {
                break;
            }

            if (
$this->debug AND $d >= 1024)
            {
                
$d 0;
                echo (
$s == '') ? '*' '|';
                
flush();
            }

            
$d++;
        } 

        if (
$this->debug
        { 
            echo 
" <-- RECEIVING END\n"
            
flush();
        } 

        return 
$read
    } 

    
/**
     * Запись данных в поток
     *
     * @access    public
     * @param    resource
     * @param    string
     * @return    bool
     */
    
public function write(&$socket$request)
    {
        if(!
is_resource($socket)) 
        {
            return 
FALSE;
        }

        
fwrite($socket$request);
    }

    
/**
     * Чтение строки данных из потока
     *
     * @access    public
     * @param    resource
     * @return    bool
     */
    
public function read_line(&$socket)
    {
        
$read '';

        while (((
$character fread($socket1)) != "\n") AND !feof($socket))
        {
            
$read .= $character;
        }

        return 
$read;
    }

    
/**
     * Закрытие потока
     *
     * @access    public
     * @param    resource
     * @return    void
     */
    
public function close($socket)
    {
        if(
is_resource($socket)) 
        {
            
fclose($socket);
        }
    }

    
/**
     * Проверка контента на правильность
     *
     * @access    public
     * @param    array
     * @param    bool
     * @return    bool
     */
    
public function validate_content($content$valid_code FALSE)
    {
        if (!isset(
$content['code']) 
            OR !isset(
$content['headers']) 
            OR !isset(
$content['body']))
        {
            return 
FALSE;
        }

        if (
$valid_code !== FALSE AND $valid_code != $content['code'])
        {
            return 
FALSE;
        }

        if (isset(
$content['headers']['Content-Length']) 
            AND (int) 
$content['headers']['Content-Length'] !== strlen($content['body']))
        {
            return 
FALSE;
        }

        return 
TRUE;
    }

    
/**
     * Генерация HTTP запроса
     *
     * @access    public
     * @param    string
     * @param    string
     * @param    array
     * @param    string
     * @return    string
     */
    
public function generate_request($method$path$headers = array(), $body '')
    {
        
$request strtoupper($method).' '.$path." HTTP/1.0\r\n";
        
$request .= $this->generate_headers($headers);

        if (
$body != '')
        {
             
$request .= $body."\r\n";
        }

        return 
$request;
    }

    
/**
     * Генерация HTTP заголовков
     *
     * @access    public
     * @param    array
     * @return    string
     */
    
public function generate_headers($headers)
    {
        if (
sizeof($headers) == 0)
        {
            return 
'';
        }

        
$headers_string '';

        foreach (
$headers as $name => $value)
        {
            if (
$name != '' AND $value != '')
            {
                if (
is_array($value))
                {
                    
$sub_headers '';

                    foreach (
$value as $sub_value)
                    {
                        
$sub_headers .= $sub_value.';';
                    }

                    
$headers_string .= $name.': '.trim($sub_headers';')."\r\n";
                }
                else
                {
                    
$headers_string .= $name.': '.$value."\r\n";
                }
            }
        }

        return 
$headers_string."\r\n";
    }

    
/**
     * Генерация URI строки
     *
     * @access    public
     * @param    array
     * @return    string
     */
    
public function generate_uri($uri)
    {
        if (
sizeof($uri) == 0)
        {
            return 
'';
        }

        
$uri_string '';

        foreach (
$uri as $name => $value)
        {
            if (
$name != '')
            {
                
$uri_string .= $name.'='.urlencode($value).'&';
            }
        }

        return 
trim($uri_string'&');
    }

    
/**
     * Парсинг HTTP заголовков
     *
     * @access    public
     * @param    string
     * @return    array
     */
    
private function parse_headers($headers)
    {
        
$lines explode("\n"str_replace(array("\r\n""\r"), "\n"$headers));

        
$headers = array();

        if (
sizeof($lines) > 0)
        {
            foreach (
$lines as $line)
            {
                
$header explode(': '$line);

                if (isset(
$header[0]) AND isset($header[1]))
                {
                    if (isset(
$headers[$header[0]]) AND !is_array($headers[$header[0]]))
                    {
                        
$headers[$header[0]] = array($headers[$header[0]]);
                    }

                    if (isset(
$headers[$header[0]]) AND is_array($headers[$header[0]]))
                    {
                        
$headers[$header[0]][] = trim($header[1]);
                    }
                    else
                    {
                        
$headers[$header[0]] = trim($header[1]);
                    }
                }
            }
        }

        return 
$headers;
    }

    
/**
     * Парсинг кода HTTP ответа
     *
     * @access    public
     * @param    string
     * @return    int
     */
    
private function parse_status_code($headers)
    {
        if (
strlen($headers) < 12)
        {
            return 
FALSE;
        }

        
$status substr($headers93);

        if (!
is_numeric($status))
        {
            return 
FALSE;
        }

        return 
$status;
    }
}

/* EOF Http.class.php */