Skip to content

Instantly share code, notes, and snippets.

@Quacky2200
Last active July 24, 2020 17:39
Show Gist options
  • Select an option

  • Save Quacky2200/74d280bf828fcd68f326a4b5aab603c3 to your computer and use it in GitHub Desktop.

Select an option

Save Quacky2200/74d280bf828fcd68f326a4b5aab603c3 to your computer and use it in GitHub Desktop.

Revisions

  1. Quacky2200 revised this gist Jan 2, 2020. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions Request.php
    Original file line number Diff line number Diff line change
    @@ -140,8 +140,6 @@ public function makeRequest($uri = "/", $params = null, $method = "GET", $mime =
    $uri .= "?" . http_build_query($params);
    }

    var_dump($this->combineHeaders($this->headers, 'string'));

    $opts = array(
    'http' => array(
    'header' => $this->combineHeaders($this->headers, 'string'),
  2. Quacky2200 revised this gist Jan 2, 2020. 1 changed file with 6 additions and 4 deletions.
    10 changes: 6 additions & 4 deletions Request.php
    Original file line number Diff line number Diff line change
    @@ -18,7 +18,7 @@ class Request {
    public $defaultMime = 'application/json';

    /**
    * Creates a RESTRequest object
    * Creates a Request object
    */
    public function __construct($protocol = null, $host = null, $path = null) {
    $this->protocol = $protocol ?: 'https';
    @@ -66,8 +66,8 @@ public function makeRequest($uri = "/", $params = null, $method = "GET", $mime =
    $uri = $this->protocol . "://" . $this->host . $this->path . $uri;

    $this->headers += array(
    "Accept", $mime,
    "User-Agent", $this->userAgent
    "Accept" => $mime,
    "User-Agent" => $this->userAgent
    );

    if ($this->authorize) {
    @@ -140,11 +140,13 @@ public function makeRequest($uri = "/", $params = null, $method = "GET", $mime =
    $uri .= "?" . http_build_query($params);
    }

    var_dump($this->combineHeaders($this->headers, 'string'));

    $opts = array(
    'http' => array(
    'header' => $this->combineHeaders($this->headers, 'string'),
    'method' => $method,
    'user_agent' => $agent,
    'user_agent' => $this->userAgent,
    'proxy' => $this->proxyURL,
    'timeout' => 5,
    ),
  3. Quacky2200 renamed this gist Jan 2, 2020. 1 changed file with 163 additions and 38 deletions.
    201 changes: 163 additions & 38 deletions APIRequest.php → Request.php
    Original file line number Diff line number Diff line change
    @@ -3,30 +3,32 @@
    * REST test scripts
    * @license MIT
    */
    class RESTRequest {
    const PROTOCOL = "https";
    const HOST = "localhost";
    const API_PATH = "/api/v1";
    class Request {

    public $username;
    public $password;
    public $protocol;
    public $host;
    public $path;
    public $proxyURL;
    public $decoders;
    public $userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36";
    public $allowInsecure = false;
    public $headers = array();
    // set to Basic for user/pass authorization
    public $authorize = null;
    public $defaultMime = 'application/json';

    /**
    * Creates a RESTRequest object
    */
    public function __construct($username, $password) {
    $this->username = $username;
    $this->password = $password;
    public function __construct($protocol = null, $host = null, $path = null) {
    $this->protocol = $protocol ?: 'https';
    $this->host = $host ?: 'localhost';
    $this->path = $path ?: '';

    $this->decoders = array(
    "application/xml" => function($res) {
    // Decode an XML response
    return simplexml_load_string($res);
    //return json_decode(json_encode((array)simplexml_load_string($res)), true);
    return json_decode(json_encode((array)simplexml_load_string($res)), true);
    },
    "application/json" => function($res) {
    // Decode a JSON response
    @@ -42,41 +44,63 @@ public function __construct($username, $password) {
    // return $result;
    // },
    );

    }

    /**
    * Make a request to the API with the following information
    * Make a request to the API with the following information.
    *
    * You can use params to contain uri query information, but you cannot use
    * both $uri and $params to create GET query parameters. It's probably best
    * to stick to $params so that it will be easier to manage them.
    *
    * @param string $uri The endpoint
    * @param array $params Extra data for the request (GET/POST data)
    * @param string $method The request type (GET/POST/PUT/DELETE)
    * @param string $format The response format (JSON/XML)
    * @return Array
    */
    public function makeRequest($uri = "/", $params = array(), $method = "GET", $mime = "application/json") {
    public function makeRequest($uri = "/", $params = null, $method = "GET", $mime = null) {

    $authorisation = null;
    if ($this->authorisation) {
    $authorisation = "{$this->username}:{$this->password}";
    // Build URL
    $uri = $this->protocol . "://" . $this->host . $this->path . $uri;

    $this->headers += array(
    "Accept", $mime,
    "User-Agent", $this->userAgent
    );

    if ($this->authorize) {
    $this->headers['Authorization'] = $this->authorize;
    }

    // Build URL
    $uri = self::PROTOCOL . "://" . self::HOST . self::API_PATH . "{$uri}";
    $params = $params ?: array();
    if ($params !== null && !is_array($params)) {
    throw new \Exception(
    'Invalid type ' . gettype($mime) .
    ', expected array for request parameters'
    );
    }

    $mime = $mime ?: $this->defaultMime;
    if ($mime !== null && !is_string($mime)) {
    throw new \Exception(
    'Invalid type ' . gettype($mime) .
    ', expected string for mime'
    );
    }

    if (function_exists('curl_init')) {
    $opts = array(
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HEADER => false,
    CURLOPT_HTTPHEADER => array(
    "Accept: {$mime}",
    "User-Agent: {$this->userAgent}"
    ),
    CURLOPT_HTTPHEADER => $this->combineHeaders($this->headers, 'array'),
    CURLOPT_FAILONERROR => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_PROXY => $this->proxyURL,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_USERPWD => $authorisation
    );

    if ($this->allowInsecure) {
    @@ -105,18 +129,20 @@ public function makeRequest($uri = "/", $params = array(), $method = "GET", $mim
    if (curl_setopt_array($ch, $opts) === false || ($data = curl_exec($ch)) === false) {
    $err = curl_error($ch);
    curl_close($ch);
    throw new RuntimeException("Error: {$err} with URL \"{$uri}\"");
    throw new RuntimeException("{$err} \"{$uri}\"");
    }
    } elseif (ini_get('allow_url_fopen')) {
    if ($authorisation) {
    $authorisation = "Authorization: Basic " . base64_encode("{$this->username}:{$this->password}") . "\n";
    }

    $accept = "Accept: {$mime}";
    if (in_array(strtoupper($method), array("PUT", "POST"))) {
    $this->headers["Content-Type"] = "application/x-www-form-urlencoded";
    $opts['http']['content'] = http_build_query($request, '', '&');
    } else if (strpos($uri, '?') === false) {
    $uri .= "?" . http_build_query($params);
    }

    $opts = array(
    'http' => array(
    'header' => $authorisation . $accept,
    'header' => $this->combineHeaders($this->headers, 'string'),
    'method' => $method,
    'user_agent' => $agent,
    'proxy' => $this->proxyURL,
    @@ -131,13 +157,6 @@ public function makeRequest($uri = "/", $params = array(), $method = "GET", $mim
    );
    }

    if (in_array(strtoupper($method), array("PUT", "POST"))) {
    $opts['http']['header'] .= "\nContent-Type: application/x-www-form-urlencoded";
    $opts['http']['content'] = http_build_query($request, '', '&');
    } else {
    $uri .= "?" . http_build_query($params);
    }

    $context = stream_context_create($opts);

    if (($data = @file_get_contents($uri, false, $context)) === false) {
    @@ -167,13 +186,120 @@ public function makeRequest($uri = "/", $params = array(), $method = "GET", $mim
    return $decoded;
    }

    /**
    * Combines headers into a string
    * @param array $headers Dictionary of header keys and values
    * @param string $type Return type
    * @return mixed String or Array containing combined KVPs
    */
    public function combineHeaders($headers, $type = 'string') {
    foreach($headers as $key => &$value) {
    if (is_string($key)) {
    $value = "$key: $value";
    }
    }

    if ($type == 'string') {
    // returns 'User-Agent: Chrome\nHost: localhost\n' etc...
    return implode(array_values($headers), "\n");
    } else if ($type == 'array') {
    // returns array('User-Agent: Chrome', 'Host: localhost'), etc...
    return array_values($headers);
    } else {
    throw new \Exception('Invalid combineHeaders type');
    }
    }

    /**
    * Parse headers from a string into an array.
    * Not yet implemented
    * @return [type] [description]
    */
    public function parseHeaders() {
    // TODO?
    throw new \Exception('Not yet implemented');
    }

    /**
    * Adds a KVP to the headers for future requests.
    * @param string $key Header key
    * @param string $value Header value
    */
    public function addHeader($key, $value) {
    $this->headers[$key] = $value;

    return $this;
    }

    /**
    * Remove a KVP from the headers for future requests.
    * @param string $key Header key
    * @param string $value Header value
    */
    public function removeHeader($key) {
    if (isset($this->headers[$key])) {
    unset($this->headers[$key]);
    }

    return $this;
    }

    /**
    * This removes any authorization header currently being used.
    * @return $this
    */
    public function removeAuthorization() {
    $this->authorize = null;

    return $this;
    }

    /**
    * Sets basic authorization for username and password
    * @param string $username User's Username
    * @param string $password User's password
    * @return $this
    */
    public function setBasicAuthorization($username, $password) {
    $this->authorize = "Basic " . base64_encode("{$username}:{$password}");

    return $this;
    }

    /**
    * Set authorization header with authorization string (e.g. token)
    * @param string $auth authorization string
    * @return $this
    */
    public function setOtherAuthorization($auth) {
    $this->authorize = $auth;

    return $this;
    }

    /**
    * Sets the default mime type.
    * @param string $mime Default mime
    * @return $this
    */
    public function setDefaultMime($mime) {
    if (is_string($mime)) {
    $this->defaultMime = $mime;
    }

    return $this;
    }

    /**
    * Adds a decoding mechanism to the supported list of decoders
    * @param string $mime The mime type in the format of <MIME_type/MIME_subtype>
    * @param closure $decodeLamda The decoding mechanism to support the mime type
    * @return $this
    */
    public function addDecoder($mime, $decodeLamda){
    $this->decoders[$mime] = $decodeLamda;

    return $this;
    }

    /**
    @@ -253,5 +379,4 @@ function arrayMergeRecursiveDistinct(array &$array1, array &$array2, $dynamicKey
    }
    return $merged;
    }
    }
    ?>
    }
  4. Quacky2200 revised this gist Jan 2, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion APIRequest.php
    Original file line number Diff line number Diff line change
    @@ -25,7 +25,7 @@ public function __construct($username, $password) {
    $this->decoders = array(
    "application/xml" => function($res) {
    // Decode an XML response
    return simplexml_load_string($res);
    return simplexml_load_string($res);
    //return json_decode(json_encode((array)simplexml_load_string($res)), true);
    },
    "application/json" => function($res) {
  5. Quacky2200 created this gist Jan 2, 2020.
    257 changes: 257 additions & 0 deletions APIRequest.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,257 @@
    <?php
    /**
    * REST test scripts
    * @license MIT
    */
    class RESTRequest {
    const PROTOCOL = "https";
    const HOST = "localhost";
    const API_PATH = "/api/v1";

    public $username;
    public $password;
    public $proxyURL;
    public $decoders;
    public $userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36";
    public $allowInsecure = false;

    /**
    * Creates a RESTRequest object
    */
    public function __construct($username, $password) {
    $this->username = $username;
    $this->password = $password;

    $this->decoders = array(
    "application/xml" => function($res) {
    // Decode an XML response
    return simplexml_load_string($res);
    //return json_decode(json_encode((array)simplexml_load_string($res)), true);
    },
    "application/json" => function($res) {
    // Decode a JSON response
    return json_decode($res, true);
    },
    "application/vnd.php.serialized" => function($res) {
    // Deserialize a PHP object
    return unserialize($res);
    },
    // "application/x-www-form-urlencoded" => function($res) {
    // // Decode a url encoded string
    // $result = $this->parseUrl(urldecode($res));
    // return $result;
    // },
    );
    }

    /**
    * Make a request to the API with the following information
    * @param string $uri The endpoint
    * @param array $params Extra data for the request (GET/POST data)
    * @param string $method The request type (GET/POST/PUT/DELETE)
    * @param string $format The response format (JSON/XML)
    * @return Array
    */
    public function makeRequest($uri = "/", $params = array(), $method = "GET", $mime = "application/json") {

    $authorisation = null;
    if ($this->authorisation) {
    $authorisation = "{$this->username}:{$this->password}";
    }

    // Build URL
    $uri = self::PROTOCOL . "://" . self::HOST . self::API_PATH . "{$uri}";

    if (function_exists('curl_init')) {
    $opts = array(
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HEADER => false,
    CURLOPT_HTTPHEADER => array(
    "Accept: {$mime}",
    "User-Agent: {$this->userAgent}"
    ),
    CURLOPT_FAILONERROR => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_PROXY => $this->proxyURL,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_USERPWD => $authorisation
    );

    if ($this->allowInsecure) {
    $opts += array(
    CURLOPT_SSL_VERIFYPEER => false,
    CURLOPT_SSL_VERIFYHOST => false,
    );
    }

    // Only send content in PUT and POST requests
    if (in_array(strtoupper($method), array("PUT", "POST"))){
    $opts[CURLOPT_POSTFIELDS] = http_build_query($params, '', '&');
    } else {
    $uri .= "?" . http_build_query($params);
    }

    // Start the connection
    $ch = curl_init($uri);

    // Make sure we can connect
    if (($ch = curl_init($uri)) === false) {
    throw new RuntimeException("Cannot connect to HOST: {$uri}");
    }

    // Try to make the request and get data out
    if (curl_setopt_array($ch, $opts) === false || ($data = curl_exec($ch)) === false) {
    $err = curl_error($ch);
    curl_close($ch);
    throw new RuntimeException("Error: {$err} with URL \"{$uri}\"");
    }
    } elseif (ini_get('allow_url_fopen')) {
    if ($authorisation) {
    $authorisation = "Authorization: Basic " . base64_encode("{$this->username}:{$this->password}") . "\n";
    }

    $accept = "Accept: {$mime}";

    $opts = array(
    'http' => array(
    'header' => $authorisation . $accept,
    'method' => $method,
    'user_agent' => $agent,
    'proxy' => $this->proxyURL,
    'timeout' => 5,
    ),
    );

    if ($this->allowInsecure) {
    $opts['ssl'] = array(
    "verify_peer" => false,
    "verify_peer_name" => false,
    );
    }

    if (in_array(strtoupper($method), array("PUT", "POST"))) {
    $opts['http']['header'] .= "\nContent-Type: application/x-www-form-urlencoded";
    $opts['http']['content'] = http_build_query($request, '', '&');
    } else {
    $uri .= "?" . http_build_query($params);
    }

    $context = stream_context_create($opts);

    if (($data = @file_get_contents($uri, false, $context)) === false) {
    $error = error_get_last()['message'];
    preg_match("/(?:HTTP request failed! HTTP\/[0-9].[0-9] ([0-9a-zA-Z ]+))/", $error, $specific);
    $error = (@$specific[1] ?: $error);
    throw new RuntimeException("Cannot connect to HOST as $error: {$uri}");
    }
    } else {
    throw new RuntimeException('No means of communication with REST API, please enable CURL or HTTP Stream Wrappers');
    }

    if (!$data) {
    throw RuntimeException("The response was empty");
    }

    $decoder = $this->getDecoder($mime);
    if (!$decoder) {
    throw new RuntimeException("No decoder is present for mime type {$mime}");
    }

    $decoded = $decoder($data);
    if (!$decoded) {
    throw new RuntimeException("The response cannot be decoded into the mime type {$mime}. Response: {$data}");
    }

    return $decoded;
    }

    /**
    * Adds a decoding mechanism to the supported list of decoders
    * @param string $mime The mime type in the format of <MIME_type/MIME_subtype>
    * @param closure $decodeLamda The decoding mechanism to support the mime type
    */
    public function addDecoder($mime, $decodeLamda){
    $this->decoders[$mime] = $decodeLamda;
    }

    /**
    * Gets a decoding lamda mechanism for a known mime type
    * @param string $mime The mime type in the format of <MIME_type/MIME_subtype>
    * @return closure The decoding mechanism
    */
    public function getDecoder($mime) {
    if (array_key_exists($mime, $this->decoders)){
    return $this->decoders[$mime];
    } else {
    return null;
    }
    }

    /**
    * Returns all known mime types supported by the REST API
    * @return array list of <MIME_type/MIME_subtype>
    */
    public function getMimeTypes(){
    return array_keys($this->decoders);
    }

    // @see https://gist.github.com/rubo77/6821632 to skip max_input_vars error

    /**
    * @param $string
    * @return array|bool
    */
    function parseUrl($string) {
    if($string==='') {
    return false;
    }
    $result = array();
    // Find the pairs "name=value"
    $pairs = explode('&', $string);
    foreach ($pairs as $pair) {
    $dynamicKey = (false !== strpos($pair, '[]=')) || (false !== strpos($pair, '%5B%5D='));
    // use the original parse_str() on each element
    parse_str($pair, $params);
    $k = key($params);
    if (!isset($result[$k])) {
    $result += $params;
    } else {
    $result[$k] = $this->arrayMergeRecursiveDistinct($result[$k], $params[$k], $dynamicKey);
    }
    }
    return $result;
    }

    /**
    * @param array $array1
    * @param array $array2
    * @param $dynamicKey
    * @return array
    */
    function arrayMergeRecursiveDistinct(array &$array1, array &$array2, $dynamicKey) {
    $merged = $array1;
    foreach ($array2 as $key => &$value) {
    if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
    $merged[$key] = $this->arrayMergeRecursiveDistinct($merged[$key], $value, $dynamicKey);
    } else {
    if ($dynamicKey) {
    if (!isset( $merged[$key])) {
    $merged[$key] = $value;
    } else {
    if (is_array($merged[$key])) {
    $merged[$key] = array_merge_recursive($merged[$key], $value);
    } else {
    $merged[] = $value;
    }
    }
    } else {
    $merged[$key] = $value;
    }
    }
    }
    return $merged;
    }
    }
    ?>