<?php
/* 
 * Basic HTTP Request Class
 * A class for performing HTTP requests without any additional libraries such as cURL.
 * 
 * @author ynori7
 * @copyright Copyright (c) 2014, halls-of-valhalla.org
 * @license http://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0 International License. 
 * 
 * Example Usage:
 * use Valhalla\CoreUtilities\Http\Request\BasicRequest;
 * $req = new BasicRequest();
 * $req->setOptions(array(
 *    'http' => array(
 *        'timeout' => 10,
 *        'header' => array('Authorization' => sprintf('Basic %s', base64_encode('user' . ':' . 'password')))
 *    )
 * ));
 * var_dump($req->post('http://halls-of-valhalla.org/test.php', array('blah'=>234)));
 */

namespace Valhalla\CoreUtilities\Http\Request;

use Valhalla\CoreUtilities\Data\Collection;

class BasicRequest implements RequestInterface {
    private $_options = array();
    /*
     * In the options/defaultOptions arrays when specifying the header, make it an array of header strings. For example:
     * $options['http']['header'] = array(
     *     'Authorization' => sprintf('Basic %s', base64_encode($user . ':' . $password)),
     *     'Content-type' => 'pplication/x-www-form-urlencoded',
     * )
     */
    private $_defaultOptions = array(
        'http' => array(
            'method' => 'GET',
            'timeout' => 30,
        ),
        'ssl' => array(
            'verify_peer'   => false,
        ),
    );
    
    public function __construct() {
        $this->_options = $this->_defaultOptions;
    }
    
    /**
     * Set extra request options to use for this instance.
     * @param array $options
     */
    public function setOptions(array $options){
        $this->_options = Collection::merge($this->_defaultOptions, $options, true);
    }
    
    /**
     * Accepts a list of HTTP headers and returns a string properly formatted with
     * carriage returns and line breaks.
     * 
     * @param array $headers
     * @return string
     */
    public function getHeaderStringFromArray(array $headers){
        $headerString = '';
        foreach($headers as $header => $value){
            $headerString .= "$header:$value\r\n";
        }
        
        return $headerString;
    }
    
    /**
     * 
     * @param string $url
     * @param array $payload
     * @return string
     */
    public function post($url, array $payload = array()){
        $payload = http_build_query($payload);
        $httpParams = array(
            'method' => 'POST',
            'content' => $payload,
            'header'=> array(
                "Content-type" => "application/x-www-form-urlencoded",
                "Content-Length" => strlen($payload),
            ),
        );
        
        return $this->doRequest($url, $httpParams);
    }

    /**
     * 
     * @param string $url
     * @return string
     */
    public function delete($url) {
        $httpParams = array(
            'method' => 'DELETE',
        );
        
        return $this->doRequest($url, $httpParams);
    }

    /**
     * 
     * @param string $url
     * @return string
     */
    public function get($url) {
        $httpParams = array(
            'method' => 'GET',
        );
        
        return $this->doRequest($url, $httpParams);
    }

    /**
     * 
     * @param string $url
     * @param array $payload
     * @return string
     */
    public function put($url, array $payload = array()) {
        $payload = http_build_query($payload);
        $httpParams = array(
            'method' => 'PUT',
            'content' => $payload,
            'header'=> array(
                "Content-type" => "application/x-www-form-urlencoded",
                "Content-Length" => strlen($payload),
            ),
        );
        
        return $this->doRequest($url, $httpParams);
    }
    
    /**
     * Perform the request with an optional set of additional HTTP parameters.
     * 
     * @param string $url
     * @param array $httpParams
     * @throws \Exception
     * @return string
     */
    public function doRequest($url, array $httpParams = array()){
        $params = $this->_options;
        $params['http'] = Collection::merge($params['http'], $httpParams, true);

        if(isset($params['http']['header'])){
            $params['http']['header'] = $this->getHeaderStringFromArray($params['http']['header']);
        }

        $ctx = stream_context_create($params);
        $fp = fopen($url, 'rb', false, $ctx);
        if (!$fp) {
          throw new \Exception("Failed to open connection to $url");
        }
        $response = stream_get_contents($fp);
        if ($response === false) {
          throw new \Exception("Failed to retrieve data from $url");
        }
        
        return $response;
    }

}