<?php
/**
 * Classe générique d'envoi de requête REST avec cURL
 * 
 *  @package openads
 */
 
class MessageSenderRest {
    
    // Variables de la classe
    private $url;
    private $username;
    private $password;
    private $curl;
    private $contentType;
    private $charset;
    private $data;
    private $max_log_size = 500;
    private $headers = array(
        'content_type' => "",
        'http_code' => "",
    );
    private $response = "";
    private $acceptedContentType = array(
        'application/xml' => 'xml',
        'text/xml' => 'xml',
        'application/json' => 'json',
        'text/json' => 'json',
        'application/html' => 'html',
        'text/html' => 'html',
        'application/pdf' => 'pdf',
        'text/pdf' => 'pdf',
        'text/plain' => 'plain',
        'image/gif' => 'gif',
        'image/jpeg' => 'jpeg',
        'image/png' => 'png',
        'application/base64' => 'base64',
    );
    
    
    /**
     * Constructeur
     */
    public function __construct($url, $username = "", $password = ""){
            
        //Initialisation des variables de la classe
        $this->url = $url;
        $this->username = $username;
        $this->password = $password;
        
        //Initialisation de la session curl
        $this->curl = curl_init();
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($this->curl, CURLOPT_BINARYTRANSFER, true);
        
        //Tente une authentification si les options ont été fournies
        if ( $this->username !== "" && $this->password !== "" ){
            curl_setopt($this->curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
            curl_setopt($this->curl, CURLOPT_USERPWD, $this->username . ':' . $this->password);
        }
    }
    
    /**
     * Lance l'appel au webservice
     * @param string $method
     * @param string $contentType
     * @param string $file
     * 
     * @return Retourne la réponse du webservice ou -1 en cas d'erreur
     */
    public function execute($method, $contentType, $data, $url = null, $headers = array()){


        // Formatage des données passées en paramètre
        $data = is_array($data)?http_build_query($data):$data;

        //
        $data_log = print_r($data, true);
        $max = $this->getMaxLogSize();
        $data_log = strlen($data_log) > $max ?
            substr($data_log,0,$max)."[...]" : $data_log;
        $this->log(
            sprintf(
                "%s / url : %s / method : %s / data : %s",
                __METHOD__,
                $this->getUrl(),
                $method,
                $data_log
            )
        );

        //
        if($url != null) {
            $this->setUrl($url);
        }
        
        //L'URL d'envoi doit être non vide
        if (strcmp($this->getUrl(), "")==0) {
            return -1;
        }
        
        // XXX
        // Méthode temporaire permettant de passer autre chose que content-type en header
        // de la requête, on fusionne $contentType et $headers
        $http_headers = array_merge(array("Content-Type: ".$contentType), $headers);
        curl_setopt ($this->getCurl(), CURLOPT_HTTPHEADER, $http_headers);

        
        //Ajoute les certaines options à la session curl selon la méthode d'envoi 
        //souhaitée
        switch ($method) {
            case 'GET':
                curl_setopt($this->getCurl(),CURLOPT_HTTPGET,true);
                break;
            case 'PUT':
                curl_setopt($this->getCurl(),CURLOPT_PUT,true);
                //Ajout du xml dans un fichier ataché à la requête
                //Crée un fichier temporaire
                $putData = tmpfile();
                //Ecrit les données dans le fichier temporaire
                fwrite($putData, $data);
                //Place le curseur au début du fichier
                fseek($putData, 0);
                //Le fichier lu par le transfert lors du chargement
                curl_setopt($this->getCurl(), CURLOPT_INFILE, $putData);
                //Taille du fichier en octet attendue
                curl_setopt($this->getCurl(), CURLOPT_INFILESIZE, strlen($data));
                break;
            case 'POST':
                curl_setopt($this->getCurl(),CURLOPT_POST,true);
                curl_setopt($this->getCurl(),CURLOPT_POSTFIELDS,$data);
                break;
            case 'DELETE':
                curl_setopt($this->getCurl(),CURLOPT_CUSTOMREQUEST,'DELETE');
                break;
            //Si aucune méthode n'a été fournie
            default:
                return -1;
        }
        // une reponse est attendue
        curl_setopt($this->getCurl(), CURLOPT_RETURNTRANSFER, true);
        //Ajoute l'url        
        curl_setopt($this->getCurl(),CURLOPT_URL,$this->getUrl());
        $this->setResponse(curl_exec($this->getCurl()));
        
        $this->treatResponse();
        //
        $response_log = print_r($this->getResponse(), true);
        $max = $this->getMaxLogSize();
        $response_log = strlen($response_log) > $max ?
            substr($response_log,0,$max)."[...]" : $response_log;
        $this->log(
            sprintf(
                "%s / url : %s / method : %s / response : %s",
                __METHOD__,
                $this->getUrl(),
                $method,
                $response_log
            )
        );
        //
        return $this->getResponse();
    }
    
    /**
     * Va traiter le retour de l'exécution d'une requête afin d'en extraire l'header 
     * et la réponse.
     */
    private function treatResponse(){
        
        $headers = array();
        //Si la réponse n'est pas vide
        if ( $this->getResponse() !== NULL ){
            
            //Récupération des informations du retour de la requête
            $response_info = curl_getinfo($this->getCurl());
            
            //Si la requête ne retourne aucune information
            if ( !is_array($response_info) ){
                return;
            }
            
            //Récupère le content-type et le code HTTP de la requête réponse
            $headers['content_type'] = $response_info['content_type'];
            $headers['http_code'] = $response_info['http_code'];
            $this->setHeaders($headers);

            $contentType = $headers['content_type'];

            if ($contentType !== '' AND $contentType !== null) {
                // Si le header content-type contient un charset et que celui-ci est différent
                // de chaine vide, alors on le met dans $charset
                $regex = "/([\w-\/]*);?\s*(?:charset=(.*))?/";
                if (preg_match($regex, $contentType, $matches)) {
                    $contentType = $matches[1];
                    if (isset($matches[2]) AND $matches[2] !== '') {
                        $this->setCharset($matches[2]);
                    }
                }
            }
            
            //Décomposition des données selon le type de retour
            $acceptedContentType = $this->getAcceptedContentType();
            $contentType = isset($acceptedContentType[$contentType])?$acceptedContentType[$contentType]: '';
            switch ($contentType) {
                case 'xml':
                    $this->setResponse((array) simplexml_load_string($this->getResponse(), 'SimpleXMLElement', LIBXML_NOCDATA));
                    break;
                case 'json':
                    $this->setResponse((array) json_decode(trim($this->getResponse()), true));
                    break;
                //Si la réponse n'est pas un des formats supportés
                default:
                                      
                    // Le header et le contenu du retour sont séparés par une ligne vide
                    $parts  = explode("\n\r",$this->getResponse());
                    
                    //Recompose la réponse dans le cas où elle contiendrait des 
                    //sauts de lignes
                    $this->setResponse("");
                    for ( $i = 0 ; $i < count($parts) ; $i++ ) {
                        
                        if( $i > 0 ) {
                            $this->setResponse($this->getResponse()."\n\r") ;
                        }
                        $this->setResponse($this->getResponse().$parts[$i]) ;
                    }
                    break;
                }
        }
        
        
    }
    
    /**
     * Destructeur
     */
    public function __destruct(){
        curl_close($this->curl);
    }
    
    /**
     * Ferme la connexion curl
     */
    public function close(){
        curl_close($this->curl);
    }
    
    // {{{ Accesseur
    /**
     * @return string 
     */
    public function getUrl() {
        return $this->url;
    }

    /**
     * @return object 
     */
    public function getCurl() {
        return $this->curl;
    }
    
    /**
     * @return array 
     */
    public function getHeaders() {
        return $this->headers;
    }
    
    /**
     * @return string 
     */
    public function getResponse() {
        return $this->response;
    }
    
    /**
     * @return numeric 
     */
    public function getResponseCode() {
        return $this->headers['http_code'];
    }
    
    /**
     * @return string 
     */
    public function getResponseContentType() {
        return $this->headers['content_type'];
    }
    
    /**
     * @return array
     */
    public function getAcceptedContentType() {
        return $this->acceptedContentType;
    }
    
    /**
     * @return string
     */
    public function getErrorMessage(){
        return curl_error($this->getCurl());
    }
    // }}}
    
    // {{{ Mutateur
    /**
     * Met à jour le header.
     * @param array $headers
     */
    public function setHeaders($headers) {
        $this->headers=$headers;
    }

    /**
     * Met à jour l'url.
     * @param string $url
     */
    public function setUrl($url) {
        $this->url=$url;
    }
    
    /**
     * Met à jour la réponse.
     * @param string $response
     */
    public function setResponse($response) {
        $this->response=$response;
    }
    
    /**
     * Met à jour les types de contenu acceptés.
     * @param array $acceptedContentType
     */
    public function setAcceptedContentType($acceptedContentType) {
        $this->acceptedContentType=$acceptedContentType;
    }

    /**
     * Met à jour le charset.
     * @param string $charset
     */
    public function setCharset($charset) {
        $this->charset=$charset;
    }

    /**
     * Accesseur retournant le charset.
     * @return string
     */
    public function getCharset() {
        return $this->charset;
    }

    /**
     * Accesseur retournant la taille maximum alloué aux données du log.
     * @return integer (en octets)
     */
    public function getMaxLogSize() {
        return $this->max_log_size;
    }

    // }}}

    /**
     *
     */
    var $log_services = null;

    /**
     *
     */
    function log($message) {
        //
        if ($this->log_services == null) {
            //
            $this->log_services = logger::instance();
        }
        //
        $this->log_services->log_to_file("services.log", "OUT - ".$message);
    }

} 

?>
