File Manager

Current Directory: /home/astoriaah/www/plugins/system/shlib/shl_packages/api
Viewing File: /home/astoriaah/www/plugins/system/shlib/shl_packages/api/request.php
<?php /** * Shlib - programming library * * @author Yannick Gaultier * @copyright (c) Yannick Gaultier 2020 * @package shlib * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL * @version 0.4.0.716 * @date 2020-12-07 */ use Joomla\String\StringHelper; defined('_JEXEC') or die; /** * HTTP request. Breaks down and store an HTTP requests into components: * * string host * string path * array query * string fragment * array request_headers * string method * * A request */ class ShlApi_Request extends \ShlBase { /** * @var array Version of this API. */ private $apiVersion = '@build_api_version_build@'; /** * @var string The version of the handling route. */ private $version; /** * @var string Unique ID for the request (uuid v4) */ private $id; /** * @var string The date/time the request was received. */ private $timestamp; /** * @var array Holds all the request parsed parts. */ protected $request = array(); /** * @var \ShlApi_Input Manages parameters obtained by parsing the route. */ protected $parameters; /** * @var array Associative array of named parameters passed in the path. */ protected $namedParameters = array(); /** * @var string The root part of the path, to be trimmed from the full path. */ private $root; /** * @var \ShlApi_Route The router that was triggered by the request */ private $activeRoute; /** * @var \ShlApi_Response Holds the response to the current request. */ protected $response; /** * @var string A name for the component that should have registered a route for this request. */ private $namespace; /** * @var bool Whether this request has been made in a secure manner, ie https when over HTTP. */ private $secure = false; /** * @var array Cache for already parsed current request. */ protected static $currentRequest = array(); /** * \ShlApi_Request constructor. Stores provided request definition, * or use current request if none provided. * * @param array | null $request * @param string $root Fully qualified root to be trimmed from request. Typically: /api_slug/namespace * @param string $namespace * @param string $version * * array( * string $url * array $data * array $headers * string $method * ) */ public function __construct($request = null, $root = null, $namespace = '', $version = '') { parent::__construct(); $this->namespace = $namespace; $this->version = $version; $this->id = \ShlSystem_Auth::uuidv4( \ShlSystem_Auth::UUID4_NO_DASHES, \ShlSystem_Auth::UUID4_LOWERCASE ); $this->response = new \ShlApi_Response( $this->id, $this->version ); /** * {year}-{month}-{day}T{hour}:{minute}:{second},{microsecond}{timezone-offset} * 2014-12-08T12:35:00Z */ $this->timestamp = \ShlSystem_Date::getUTCNow('Y-m-d') . 'T' . \ShlSystem_Date::getUTCNow('H:i:s,u') . 'Z'; $this->root = trim($root, '/'); $this->parseCurrentRequest(); $this->parseRequest( $request ); } /** * Parse a request, or the current HTTP request if none provided. * * @param array $requestDef * * array( * string $url * array $data * array $headers * string $method * ) * * @return $this */ protected function parseRequest($requestDef = null) { if (empty($requestDef)) { $this->request = self::$currentRequest[$this->root]; } else { $url = wbArrayGet($requestDef, 'url'); $uri = $this->platform->getUri($url); $path = wbLTrim( trim($uri->getPath(), '/'), trim($uri->base(true), '/') ); $path = trim($path, '/'); $path = wbLTrim( $path, $this->root ); $pathSegments = explode( '/', trim($path, '/') ); $parsedRequest = array( 'url' => $uri->toString(), 'path' => $path, 'path_segments' => $pathSegments, 'query' => new \ShlApi_Input( $uri->getQuery(true) ), 'method' => strtoupper( wbArrayGet( $requestDef, 'method', 'GET' ) ), 'headers' => wbArrayGet( $requestDef, 'headers', array() ), 'secure' => wbArrayGet( $requestDef, 'secure', $this->secure ), 'data' => wbArrayGet( $requestDef, 'data', array() ) ); $this->request = $this->applyMethodOverrides( $parsedRequest ); } return $this; } /** * Check for method override header, and update the request method * accordingly - only for POST requests. * * @param array $parsedRequest */ private function applyMethodOverrides($parsedRequest) { $parsedRequest['original_method'] = $parsedRequest['method']; $currentMethod = wbArrayGet( $parsedRequest, 'method', 'GET' ); if ('POST' != $currentMethod) { return $parsedRequest; } // look for an override header $overrideHeader = wbArrayGet( $parsedRequest, array( 'headers', 'X-HTTP-METHOD-OVERRIDE' ) ); if ( empty($overrideHeader) || !in_array( $overrideHeader, array( 'PUT', 'DELETE', 'PATCH' ) )) { return $parsedRequest; } // valid override, update the request $parsedRequest['method'] = $overrideHeader; return $parsedRequest; } /** * Get and store the current HTTP request. * * return $this; */ private function parseCurrentRequest() { $sig = is_null($this->root) ? '__current__http__request__' : $this->root; if (!isset(self::$currentRequest[$sig])) { $uri = $this->platform->getUri(); $path = wbLTrim( trim($uri->getPath(), '/'), trim($uri->base(true), '/') ); $path = trim($path, '/'); $path = wbLTrim( $path, $this->root ); $pathSegments = explode( '/', trim($path, '/') ); $parsedRequest = array( 'url' => $uri->toString(), 'path' => $path, 'path_segments' => $pathSegments, 'query' => new \ShlApi_Input( $uri->getQuery(true) ), 'method' => $this->platform->getMethod(), 'secure' => 'https' == $uri->getScheme(), 'headers' => \ShlSystem_Http::getAllHeaders(), 'data' => $this->parseBody() ); self::$currentRequest[$sig] = $this->applyMethodOverrides( $parsedRequest ); } return $this; } /** * Parse de Json-encoded request body. * * @return array */ private function parseBody() { $requestBody = file_get_contents('php://input'); $body = json_decode( $requestBody, true ); if (empty($body)) { $body = array(); } return $body; } /** * Builds a routed URL from an API path. An optional associative array of query vars, or a string directly, * can be appended. * If namespace and version are not supplied, those from the currently matched route will be used, if a route has * been matched. * * @param string $path The API request path: /aliases/12 * @param array $query Query vars as an associative array. * @param string $root Optional api slug, namespace and version string (ie /wbl_api/wbredir/v1) * @param bool $fullyQualified Whether the full scheme+host should be prepended to the resulting URL. */ public function routeLink($path = null, $query = null, $root = null, $fullyQualified = true) { $path = empty($path) ? $this->getPath() : StringHelper::trim($path); $query = is_null($query) ? $this->getQuery()->getArray() : $query; if (!empty($query)) { $query = http_build_query($query); $separator = strpos($path, '?') === false ? '?' : '&'; $query = $separator . $query; } else { $query = ''; } $root = empty($root) ? $this->root : StringHelper::trim($root); $domain = $fullyQualified ? $this->platform->getRootUrl($pathOnly = false) : ''; $root = StringHelper::trim($root, '/'); $path = StringHelper::trim($path, '/'); $domain = StringHelper::rtrim($domain, '/'); $fullPath = wbSlashJoin( $domain, $root, $path ) . $query; return $fullPath; } /** * Getter for the request unique Id. * * @return string */ public function getId() { return $this->id; } /** * Getter for the request namespace; * * @return string */ public function getNamespace() { return $this->namespace; } /** * Getter for the date/time the request was received, as a string. * * @return string */ public function getTimestamp() { return $this->timestamp; } /** * Getter for the request method. * * @return string */ public function getMethod() { return wbArrayGet( $this->request, 'method', 'GET' ); } /** * Getter for the request original method, before any override. * * @return string */ public function getOriginalMethod() { return wbArrayGet( $this->request, 'original_method', 'GET' ); } /** * Getter for the host name. * * @return string */ public function getHost() { return wbArrayGet( $this->request, 'host', '' ); } /** * Whether the request was made over a secure connection. * * @return string */ public function isSecure() { return $this->secure; } /** * Getter for the request path. * * @return string */ public function getPath() { return trim( wbArrayGet( $this->request, 'path', '' ), '/' ); } /** * Getter for the route object that was triggered, if any. * * @return \ShlApi_Route */ public function getActiveRoute() { return $this->activeRoute; } /** * Getter for the path segments. * * @return array Path segments as an array */ public function getPathSegments() { return wbArrayGet( $this->request, 'path_segments', array() ); } /** * Returns the Nth segment in the requested path, starting from 1. * * @param int $position The numerica position of the segment, starts at 1. * * @return string */ public function getPathSegment($position = 1) { $position = (int) $position; if ($position < 1) { throw new \Exception('Invalid path segment requested'); } $segments = $this->getPathSegments(); if (empty($segments) || empty($segments[$position - 1])) { return ''; } return $segments[$position - 1]; } /** * Getter for the query variables, as an associative array. * * @return \ShlApi_Input */ public function getQuery() { $query = wbArrayGet( $this->request, 'query' ); // whitelist the query $query = $this ->getActiveRoute() ->filterQueryVariables( $query ); return $query; } /** * Getter for the request headers as an associative array. * * @return array */ public function getHeaders() { return wbArrayGet( $this->request, 'headers', array() ); } /** * Getter for a single request header value. * * @param string Header name, case insensitive. * @param string Default value if header not defined. * * @return string */ public function getHeader($name, $default = '') { return wbArrayGet( $this->request, array('headers', strtoupper($name)), $default ); } /** * Getter for the request fragment (without leading #). * * @return string */ public function getFragment() { return wbArrayGet( $this->request, 'fragment', '' ); } /** * Get the parameters object obtained from parsing the route. * * @return \ShlApi_Input */ public function getParameters() { return $this->parameters; } /** * Getter for the request body content, parsed into an array. * * @return array */ public function getBody() { return wbArrayGet( $this->request, 'data', array() ); } /** * Set the route that was activated for this request. * * @param \ShlApi_Route $route * * @return $this */ public function setActiveRoute($route) { $this->activeRoute = $route; return $this; } /** * Set the parameters object obtained from parsing the route. * * @param \ShlApi_Input $parameters An object to manage parameters in parsed route. * * @return $this */ public function setParameters($parameters) { $this->parameters = $parameters; return $this; } /** * Set the HTTP status code to use in response. * * @param int $status The HTTP status code. * * @return $this */ public function setResponseStatus($status) { $this->response->withStatus($status); return $this; } /** * Set the data to use in response. * * @param array $data Data to use in response * * @return $this */ public function setResponseData($data) { $this->response->withData($data); return $this; } /** * Set the date/time at which the resource was last modified * * @param int $lastModified A unix timestamp. * * @return $this */ public function setLastModified($lastModified) { $this->response->withLastModified($lastModified); return $this; } /** * Set a list of errors to the response. * * @param array $errors List of associative array. * * @return $this */ public function addResponseErrors($errors) { $this->response->withErrors($errors); return $this; } /** * Set a list of links related to the response. * * @param array $errors List of links. * * @return $this */ public function addResponseLinks($links) { $this->response->withLinks($links); return $this; } /** * Add/set a list of headers to output. * * @param arary $headers Associate array of headers name => headers value * * @return $this */ public function addResponseHeaders($headers) { $this->response->withHeaders($headers); return $this; } /** * Add/sets a list of meta data to be included in the response body. * * @param array $meta Associative array of meta data, ie array('next' => * 'https://www.weeblrpress.com/aliases?page=2&per_page=10') * * @return $this */ public function addResponseMeta($meta) { $this->response->withMeta($meta); return $this; } /** * Proxy to render the response to this request. */ public function respond() { $this->response->render(); } /** * Proxy to get the response data for this request. */ public function getResponse() { return $this->response->renderData(); } }