Login   Register  
PHP Classes
elePHPant
Icontem

File: gCurl/gCurlResponse.class.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Grigori Kochanov  >  gCurl  >  gCurl/gCurlResponse.class.php  >  Download  
File: gCurl/gCurlResponse.class.php
Role: Class source
Content type: text/plain
Description: process HTTP response headers and assingn handlers
Class: gCurl
Perform HTTP requests using Curl
Author: By
Last change: Accessible without user login
Date: 6 years ago
Size: 14,486 bytes
 

Contents

Class file image Download
<?php
/**
 * class represents the reply received from the remote server
 *
 * @package gCurl
 * @author Grigori Kochanov
 * @version 2
 */
class gCurlResponse{
    /**
     * Response body
     *
     * @var string
     */
    public $body = '';

    /**
     * HTTP response headers parsed
     *
     * @var array
     */
    public $headers= array('len'=>0);
    
    /**
     * HTTP response status code
     *
     * @var int
     */
    public $status_code;
    
    /**
     * HTTP response class
     *
     * @var int
     */
    public $status_class;
    
    /**
     * Content type of the response body
     *
     * @var string
     */
    public $content_type;
    
    /**
     * Cookies from the response conveniently parsed
     *
     * @var array
     */
    public $cookies=array();

    /**
     * Instance of the implementation of the gCurlHandlers abstract class
     *
     * @var gCurlHandlers
     */
    public $gCurlHandlers;
    
    /**
     * Curl connection handler
     *
     * @var resource
     */
    public $ch;
    
    /**
     * Instance of an gURI class with URI details
     *
     * @var gURI
     */
    public $URI;

    /**
     * Ignore the content-type response header, report the predefined one
     *
     * @var string
     */
    protected $force_content_type='';
        
    /**
     * Response headers handler
     *
     * @var array
     */
    private $headers_handler;

    /**
     * Response body handler function
     *
     * @var array
     */
    private $body_handler;
    
    /**
     * Cookies handler
     *
     * @var array
     */
    private $cookies_handler;
    
    /**
     * Binary flags to define parameters
     *
     * @var int
     */
    private $flags;

    /**
     * Class constructor - needs a Curl handler and a gURI instance as parameters
     *
     * @param resource $ch
     * @param gURI $URI
     */
    function __construct($ch,gURI $URI){
        $this->ch = $ch;
        $this->URI = $URI;
    }
    /**
     * Check the stream for the eof status
     *
     * @return bool
     */
    function isEof(){
        return $this->flags& gCurl::FLAG_EOF;
    }

    /**
     * Set EOF flag for the response
     *
     */
    function setEof(){
        $this->flags|=RESPONSE_HTTP_EOF;
    }

    /**
     * Get header values by header name (case insensitive)
     * 
     * @param string header_name
     * @return array
     */
    public function getHeaderValues($header_name){
        //case insensitive search ...
        $header_name = strtolower($header_name);

        if (isset($this->headers[$header_name])){
            return $this->headers[$header_name];
        }else{
            return array();
        }
    }

    /**
     * Get single header value by header name (case insensitive)
     * Returns NULL if the requested header was not received
     * 
     * @param string header_name
     * @return string
     */
    public function getHeaderByName($header_name){
        //case insensitive search ...
        $header_name = strtolower($header_name);

        if (isset($this->headers[$header_name])){
            if (is_array($this->headers[$header_name])){
                return $this->headers[$header_name][0];
            }elseif ($this->headers[$header_name]){
                return $this->headers[$header_name];
            }
        }else{
            return null;
        }
    }

    /**
     * The function is called for each header line
     *
     * @param resource ch - Curl handler resource
     * @param string header_line - line of a header
     */
    public function headersHandler($ch, $header_line){
        //the length of the header should be returned
        $header_len = strlen($header_line);

        //first call, check if it's an HTTP response status line
        if ($this->flags ^ gCurl::FLAG_HTTP_OK){

            $regexp = "~^HTTP/1\..\s(\d{3})~";
            if (!preg_match ($regexp, $header_line, $found)){
                //Non-HTTP Response Heade
                throw new gCurlException(110);
            }else{
                //ok, it's HTTP
                $this->flags |= gCurl::FLAG_HTTP_OK;
                //get response codes
                $this->headers['status-line'] = rtrim($header_line);
                $this->headers['status-code'] = $this->status_code = $found[1];
                $this->headers['status-class'] = $this->status_class = $this->status_code[0];
                $this->headers['len']=0;
            }
        }


        //end of headers?
        elseif($header_len<3 && ( $header_line[0] == "\r" || $header_line[0] == "\n")){
            //Call headers handler and assign the body handler
            
            //The content type response header can be ignored
            if (!empty($this->force_content_type)){
                $this->headers['content-type'] = $this->content_type = $this->force_content_type;
            }
            //handlers are processed and defined here
            if ($this->gCurlHandlers){
                //call headers handler
                $this->gCurlHandlers->headersHandler($this->headers);
                if ($this->cookies){
                     //call cookies handler
                    $this->gCurlHandlers->cookiesHandler($this->cookies);
                }
                //if the body handler is defined - assign it to CURL
                if ($this->gCurlHandlers->bodyHandlerName ){
                    $body_handler = array($this->gCurlHandlers,$this->gCurlHandlers->bodyHandlerName);
                    if (!is_callable($body_handler)){
                        throw new gCurlException(20);
                    }
                    curl_setopt($this->ch,CURLOPT_WRITEFUNCTION,$body_handler);
                }
            }
        }


        //maybe it's a continue of the previous header?
        elseif($this->headers['len'] >1 && ($header_line[0]==' ' || $header_line[0]=="\t")){
            $last_num = $this->headers['len']--;
            //create the new full header line
            $full_header = $this->headers[$last_num]['name'] .': '.$this->headers[$last_num]['value'] .' '.trim($header_line);
            // reprocess it
            $last_header_name = $this->headers[$last_num]['name'];
            $this->headers[$last_num]['value']=null;
            $this->headers[$last_header_name]['len']--;
            //recursive call - reproduce the header
            $this->headersHandler($ch,$full_header);
        }
        
        //just a generic header line
        else{
            //increase header counter
            $header_num = ++$this->headers['len'];

            //new header - parse it
            $header = explode(':',$header_line,2);
            if (isset($header[1])){
                //header is correct, has a value after the colon
                $name = strtolower(rtrim($header[0]));
                $value = trim($header[1]);
                //create the enumerated list of headers
                $this->headers[$header_num] = array('name'=>$name,'value'=>$value );
                //create the associative array of headers
            }else {
                //it is an invalid header
                $name = 'invalid';
                $value = rtrim($header_line);
            }

            //create the associative array for faster search
            //link the array to the headers value
            if ( empty ($this->headers[$name]['len']) ){
                $this->headers[$name] = array('len' => 1,0=>$this->headers[$header_num]['value']);
            }else{
                $len = ++$this->headers[$name]['len'];
                $this->headers[$name][$len-1] = $this->headers[$header_num]['value'];
            }

            //create shortcuts for the frequently used headers and parse cookies
            switch ($name){
                case 'content-type':
                    $this->headers['content-type'] = $value;
                    $semicolon = strpos($value,';');
                    $charset = '';
                    if($semicolon){
                        $content_type = substr($value,0,$semicolon);
                        $rest = ltrim(substr($value,$semicolon+1));
                        if (substr($rest,0,7) == 'charset'){
                            $charset = substr($rest,8);
                        }
                    }else{
                        $content_type = $value;
                    }
                    $this->headers['ctype'] = $this->content_type = $content_type;
                    $this->headers['charset'] = $charset;
                    break;
                case 'last-modified':
                    $this->headers['last-modified'] = $value;
                    break;
                case 'content-length':
                    $this->headers['content-length'] =$value;
                    break;
                case 'content-disposition':
                    $this->headers['content-disposition']=$value;
                    break;
                case 'set-cookie':
                    $this->parseCookie($value);
                    break;
                //redirect detected
                case 'location':
                    $this->headers['location']=$value;
                    //body can be skipped
                    curl_setopt($this->ch,CURLOPT_NOBODY,true);
                    break;
            }

        }
        return $header_len;
    }

    /**
     * Parses the cookie headers
     *
     * @param string $cookie_header_string
     */
    private function parseCookie($cookie_header_string){
        $cookie=array('name'=>'','value'=>'','expires'=>null,'path'=>'/','domain'=>$this->URI->host,'secure'=>null);

        $cookie_parts = explode(';',$cookie_header_string);
        $cookie_parts_len=sizeof($cookie_parts);

        //separate cookie name
        $eq_sign=strpos($cookie_parts[0],'=');
        if(!$eq_sign){
            //invalid cookie
            return;
        }

        // fetch cookie name and value
        $cookie['name']=rtrim(urldecode(substr($cookie_parts[0],0,$eq_sign)));
        $cookie['value']=urldecode(substr($cookie_parts[0],$eq_sign+1));

        //find other parameters of cookie - expires, domain, path, secure
        for ($part=1;$part<$cookie_parts_len;++$part){
            $eq_sign = strpos($cookie_parts[$part],'=');
            if(!$eq_sign){
                //this parameter has no value, just name
                if (trim($cookie_parts[$part])=='secure'){
                    $cookie['secure']=true;
                }
                continue;
            }
            //check if this is the cookie parameter, if yes - parse it
            $param=strtolower(trim(substr($cookie_parts[$part],0,$eq_sign)));
            $value=substr($cookie_parts[$part],$eq_sign+1);
            switch ($param){
                case 'expires':
                    if (($expires = strtotime(trim($value))) != -1){
                        $cookie['expires_ts'] = $expires;
                        $cookie['expires'] = date('Y-m-d H:i:s', $expires);
                        $cookie['expires_gmt'] = $value;
                    }
                    break;
                case 'max-age':
                    if (is_numeric($value)){
                        $cookie['expires'] = date('Y-m-d H:i:s', time()+$value);
                    }elseif ($expires = strtotime($value) != -1){
                        $cookie['expires'] = date('Y-m-d H:i:s', $expires);
                    }
                    break;
                case 'domain':
                    if ($value){
                        $cookie_domain_dots_count = substr_count($value,'.');
                        $domain_dots_count = substr_count($this->URI->host,'.');
                        $cookie_domain_parts = explode('.',$value,$cookie_domain_dots_count--);
                        $domain_parts = explode('.',$this->URI->host,$domain_dots_count--);
                        $accept_domain = true;
                        while($domain_dots_count>=0 && $cookie_domain_dots_count>=0){
                            if ($cookie_domain_parts[$cookie_domain_dots_count] != $domain_parts[$domain_dots_count] && strlen($cookie_domain_parts[$cookie_domain_dots_count])){
                                $accept_domain=false;
                                break;
                            }
                            $cookie_domain_dots_count--;
                            $domain_dots_count--;
                        }
                        $accept_domain && $cookie['domain'] = $value;
                    }
                    break;
                default:
                    $cookie[$param]=substr($cookie_parts[$part],$eq_sign+1);
            }
        }

        $this->cookies[]=$cookie;

    }

    /**
     * Force reporting the data as given content-type
     *
     * @param string $content_type
     */
    function forceContentType($content_type){
        if ($content_type == 'css'){
            $content_type = 'text/css';
        }elseif ($content_type == 'js'){
            $content_type = 'text/javascript';
        }

        $this->force_content_type=strtolower($content_type);
    }

    /**
     * Pass the object implementing the handlers
     * 
     * @param gCurlHandlers $Handlers
     */
    function setHandlers(gCurlHandlers $Handlers=null){
        if (!$Handlers){
            return ;
        }
        $this->gCurlHandlers = $Handlers;
    }

    /**
     * Clean all fields, flags and handlers
     *
     */
    function cleanup(){
        $this->body=$this->content_type=$this->force_content_type='';
        $this->flags=$this->status_class=$this->status_code=0;
        $this->cookies=array();
        $this->headers=array('len'=>0);
        $this->body_handler=$this->cookies_handler=$this->headers_handler=$this->gCurlHandlers=null;
    }
    
    /**
     * return the response body when the object is echo-ed or casted to string
     *
     * @return string
     */
    function __toString(){
        return $this->body;
    }
//end of the class
}