diff --git a/.github/workflows/php83.yaml b/.github/workflows/php83.yaml index 8ecb8080..56bbbd11 100644 --- a/.github/workflows/php83.yaml +++ b/.github/workflows/php83.yaml @@ -32,8 +32,6 @@ jobs: name: Code Quality needs: test uses: WebFiori/workflows/.github/workflows/quality-sonarcloud.yaml@main - with: - coverage-file: 'php-8.3-coverage.xml' secrets: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitignore b/.gitignore index 594e91d1..7742743f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ php-cs-fixer-v2.phar php-cs-fixer.phar .php-cs-fixer.cache *.Identifier +/tests/.phpunit.cache +/.vscode diff --git a/WebFiori/Http/APIFilter.php b/WebFiori/Http/APIFilter.php index 67e63f05..ea9551a1 100644 --- a/WebFiori/Http/APIFilter.php +++ b/WebFiori/Http/APIFilter.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2019 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE diff --git a/WebFiori/Http/APITestCase.php b/WebFiori/Http/APITestCase.php index 77bcfc5e..30c92089 100644 --- a/WebFiori/Http/APITestCase.php +++ b/WebFiori/Http/APITestCase.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2024 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE @@ -294,6 +294,7 @@ private function unset(array &$arr, array $params, WebServicesManager $m, array $_SERVER['HTTP_'.strtoupper($trHeader)] = $trVal; } } + $m->setRequest(Request::createFromGlobals()); $m->process(); foreach ($params as $key => $val) { diff --git a/WebFiori/Http/AbstractWebService.php b/WebFiori/Http/AbstractWebService.php index 25eae1fb..f6b97bb3 100644 --- a/WebFiori/Http/AbstractWebService.php +++ b/WebFiori/Http/AbstractWebService.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2019 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE @@ -36,7 +36,12 @@ abstract class AbstractWebService implements JsonI { * */ const I = 'info'; - + /** + * A constant which is used to indicate that the message that will be + * sent is of type success. + * + */ + const S = 'success'; /** * The name of the service. * @@ -65,6 +70,13 @@ abstract class AbstractWebService implements JsonI { * */ private $reqMethods; + /** + * The request instance used by the service. + * + * @var Request + * + */ + private $request; /** * This is used to indicate if authentication is required when the service * is called. @@ -123,6 +135,7 @@ public function __construct(string $name) { $this->requireAuth = true; $this->sinceVersion = '1.0.0'; $this->serviceDesc = ''; + $this->request = Request::createFromGlobals(); } /** * Returns an array that contains all possible requests methods at which the @@ -326,10 +339,21 @@ public final function addResponseDescription(string $description) { * ('basic', 'bearer', 'digest', etc...). The 'credentials' will contain * the credentials which can be used to authenticate the client. * - * @throws InvalidArgumentException */ public function getAuthHeader() { - return Request::getAuthHeader(); + if ($this->request !== null) { + return $this->request->getAuthHeader(); + } + return null; + } + + /** + * Sets the request instance for the service. + * + * @param mixed $request The request instance (Request, etc.) + */ + public function setRequest($request) { + $this->request = $request; } /** * Returns the description of the service. @@ -690,11 +714,11 @@ public function send(string $contentType, $data, int $code = 200) { * will be not included in response. Default is empty string. Default is null. * */ - public function sendResponse(string $message, string $type = '', int $code = 200, mixed $otherInfo = '') { + public function sendResponse(string $message, int $code = 200, string $type = '', mixed $otherInfo = '') { $manager = $this->getManager(); if ($manager !== null) { - $manager->sendResponse($message, $type, $code, $otherInfo); + $manager->sendResponse($message, $code, $type, $otherInfo); } } /** diff --git a/WebFiori/Http/HeadersPool.php b/WebFiori/Http/HeadersPool.php index 6e742b88..b8b6eac0 100644 --- a/WebFiori/Http/HeadersPool.php +++ b/WebFiori/Http/HeadersPool.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2022 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE diff --git a/WebFiori/Http/HttpCookie.php b/WebFiori/Http/HttpCookie.php index babfff05..53d7708a 100644 --- a/WebFiori/Http/HttpCookie.php +++ b/WebFiori/Http/HttpCookie.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2022 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE @@ -57,7 +57,7 @@ class HttpCookie { *
  • path: /
  • *
  • secure: true
  • *
  • http only: true
  • - *
  • domain: The domain at which the library is operating from.
  • + *
  • domain: The domain at which the cookie is operating from.
  • *
  • same site: Lax
  • *
  • expires: 0
  • *
  • value: sha256 hash
  • @@ -68,7 +68,7 @@ public function __construct() { $this->cookieName = 'new-cookie'; $this->path = '/'; $this->secure = true; - $this->domain = Request::getUri()->getHost(); + $this->domain = null; $this->sameSite = 'Lax'; $this->val = hash('sha256', date('Y-m-d H:i:s')); $this->expires = 0; diff --git a/WebFiori/Http/HttpHeader.php b/WebFiori/Http/HttpHeader.php index 58dccaf8..19c38709 100644 --- a/WebFiori/Http/HttpHeader.php +++ b/WebFiori/Http/HttpHeader.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2022 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE diff --git a/WebFiori/Http/HttpMessage.php b/WebFiori/Http/HttpMessage.php new file mode 100644 index 00000000..7e9a6222 --- /dev/null +++ b/WebFiori/Http/HttpMessage.php @@ -0,0 +1,167 @@ +headersPool = new HeadersPool(); + $this->body = ''; + $this->protocolVersion = '1.1'; + $this->requestMethod = 'GET'; + } + + /** + * Returns the headers pool. + * + * @return HeadersPool + */ + public function getHeadersPool() : HeadersPool { + return $this->headersPool; + } + + /** + * Returns the value(s) of specific HTTP header. + * + * @param string $name The name of the header. + * + * @return array + */ + public function getHeader(string $name) : array { + return $this->headersPool->getHeader($name); + } + + /** + * Returns an array that contains all headers. + * + * @return array + */ + public function getHeaders() : array { + return $this->headersPool->getHeaders(); + } + + /** + * Checks if specific header exists. + * + * @param string $name The name of the header. + * @param string|null $val Optional header value to check. + * + * @return bool + */ + public function hasHeader(string $name, ?string $val = '') : bool { + return $this->headersPool->hasHeader($name, $val); + } + + /** + * Removes specific header. + * + * @param string $name The name of the header. + * @param string|null $val Optional header value to remove. + * + * @return bool + */ + public function removeHeader(string $name, ?string $val = '') : bool { + return $this->headersPool->removeHeader($name, $val); + } + + /** + * Adds a header to the message. + * + * @param string $name The name of the header. + * @param string $value The value of the header. + * @param string|null $replaceValue Optional value to replace. + * + * @return bool + */ + public function addHeader(string $name, string $value, ?string $replaceValue = '') : bool { + return $this->headersPool->addHeader($name, $value, $replaceValue); + } + + /** + * Gets the body of the message. + * + * @return string + */ + public function getBody() : string { + return $this->body; + } + + /** + * Sets the body of the message. + * + * @param string $body + */ + public function setBody(string $body) { + $this->body = $body; + } + + /** + * Gets the protocol version. + * + * @return string + */ + public function getProtocolVersion() : string { + return $this->protocolVersion; + } + + /** + * Sets the protocol version. + * + * @param string $version + */ + public function setProtocolVersion(string $version) { + $this->protocolVersion = $version; + } + + /** + * Gets the request method. + * + * @return string + */ + public function getRequestMethod() : string { + return $this->requestMethod; + } + + /** + * Sets the request method. + * + * @param string $method + */ + public function setRequestMethod(string $method) { + $this->requestMethod = $method; + } +} diff --git a/WebFiori/Http/ManagerInfoService.php b/WebFiori/Http/ManagerInfoService.php index 44c98d62..0260f240 100644 --- a/WebFiori/Http/ManagerInfoService.php +++ b/WebFiori/Http/ManagerInfoService.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2020 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE diff --git a/WebFiori/Http/ObjectMapper.php b/WebFiori/Http/ObjectMapper.php index 4f69c7e4..4ab73808 100644 --- a/WebFiori/Http/ObjectMapper.php +++ b/WebFiori/Http/ObjectMapper.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2023 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/.github/blob/main/LICENSE diff --git a/WebFiori/Http/ParamOption.php b/WebFiori/Http/ParamOption.php index e27529f4..fa53a1f7 100644 --- a/WebFiori/Http/ParamOption.php +++ b/WebFiori/Http/ParamOption.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2024 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE diff --git a/WebFiori/Http/ParamType.php b/WebFiori/Http/ParamType.php index 2b4e6843..932a4db7 100644 --- a/WebFiori/Http/ParamType.php +++ b/WebFiori/Http/ParamType.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2019 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE diff --git a/WebFiori/Http/Request.php b/WebFiori/Http/Request.php index 9b567781..3c130cea 100644 --- a/WebFiori/Http/Request.php +++ b/WebFiori/Http/Request.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2019 Ibrahim BinAlshikh + * Copyright (c) 2024 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE @@ -12,387 +12,262 @@ use InvalidArgumentException; /** - * A class that represents HTTP client request. - * The developer can use this class to access basic information about a - * request. Note that it does not comply with PSR-7 in all aspects. - * @author Ibrahim + * Modern HTTP request class that extends HttpMessage. * + * @author Ibrahim */ -class Request { - /** - * An array that contains the names of request methods. - * - * This array contains the following strings: - * - * - * @var array An array that contains the names of request methods. - * - */ - const METHODS = [ - 'GET', - 'HEAD', - 'POST', - 'PUT', - 'DELETE', - 'TRACE', - 'OPTIONS', - 'PATCH', - 'CONNECT' - ]; - /** - * - * @var HeadersPool - * - */ - private $headersPool; - /** - * - * @var Response - * - */ - private static $inst; +class Request extends HttpMessage { - private function __construct() { - $this->headersPool = new HeadersPool(); + public function __construct() { + parent::__construct(); } + /** - * Returns an instance of the class. + * Creates a Request instance from global variables. * * @return Request + */ + public static function createFromGlobals() : Request { + $request = new Request(); + $request->extractHeaders(); + $request->setRequestMethod($request->getMethodFromGlobals()); + $request->setBody(file_get_contents('php://input')); + + return $request; + } + /** + * Returns an associative array of request headers. * + * @return array The indices of the array will be headers names and the + * values are sub-arrays. Each array contains the values of the header. */ - public static function get() { - if (self::$inst === null) { - self::$inst = new Request(); + public function getHeadersAssoc() : array { + $retVal = []; + $headers = $this->getHeaders(); + + foreach ($headers as $headerObj) { + if (!isset($retVal[$headerObj->getName()])) { + $retVal[$headerObj->getName()] = []; + } + $retVal[$headerObj->getName()][] = $headerObj->getValue(); } - return self::$inst; + return $retVal; } /** - * Returns an object that contains the value of the header 'authorization'. - * - * @return AuthHeader|null The object will have two primary attributes, the first is - * the 'scheme' and the second one is 'credentials'. The 'scheme' - * will contain the name of the scheme which is used to authenticate - * ('basic', 'bearer', 'digest', etc...). The 'credentials' will contain - * the credentials which can be used to authenticate the client. + * Returns authorization header. * - * @throws InvalidArgumentException + * @return AuthHeader|null */ - public static function getAuthHeader() { - $header = self::getHeader('authorization'); - - if (count($header) >= 1) { + public function getAuthHeader() { + $header = $this->getHeader('authorization'); + + if (count($header) == 1) { return new AuthHeader($header[0]); } - + return null; } + /** - * Returns the IP address of the user who is connected to the server. - * - * @return string The IP address of the user who is connected to the server. - * The value is taken from the array $_SERVER at index 'REMOTE_ADDR'. - * If the IP address is invalid, empty string is returned. + * Returns the IP address of the client. * + * @return string */ - public static function getClientIP() : string { + public function getClientIP() : string { if (!isset($_SERVER['REMOTE_ADDR'])) { return '127.0.0.1'; } - $ip = filter_var($_SERVER['REMOTE_ADDR'],FILTER_VALIDATE_IP); - - if ($ip == '::1') { + + $ip = $_SERVER['REMOTE_ADDR']; + + if ($ip === '::1') { return '127.0.0.1'; - } else { - return $ip; } + + $validated = filter_var($ip, FILTER_VALIDATE_IP); + + return $validated !== false ? $validated : ''; } - /** - * Returns request content type. - * - * @return string|null The value of the header 'content-type' in the request. - * - */ - public static function getContentType() { - $c = isset($_SERVER['CONTENT_TYPE']) ? filter_var($_SERVER['CONTENT_TYPE']) : null; - if ($c !== null && $c !== false) { - return trim(explode(';', $c)[0]); - } - - return null; - } /** - * Returns HTTP header given its name. - * - * @param string $name The name of the header. + * Returns the content type header value. * - * @return array If a header which has the given name exist, - * the method will return all header values as an array. If the header - * does not exist, the array will be empty. + * @return string|null */ - public static function getHeader(string $name) : array { - self::getHeaders(); - - return self::getHeadersPool()->getHeader($name); + public function getContentType() { + return isset($_SERVER['CONTENT_TYPE']) ? filter_var($_SERVER['CONTENT_TYPE']) : null; } /** - * Returns HTTP request headers. - * - * This method will try to extract request headers using two ways, - * first, it will check if the method 'apache_request_headers()' is existed or not. If it does, then request headers will be taken from - * there. If it does not exist, it will try to extract request headers - * from the super global $_SERVER. - * - * @return array An array of request headers. Each header is represented - * as an object of type HttpHeader. + * Returns HTTP request method. * + * @return string */ - public static function getHeaders() : array { - if (defined('__PHPUNIT_PHAR__') || self::get()->headersPool->getHeaders() == [] || http_response_code() === false) { - //Always Refresh headers if in testing environment. - self::extractHeaders(); - } - - return self::getHeadersPool()->getHeaders(); - } - /** - * Returns an associative array of request headers. - * - * @return array The indices of the array will be headers names and the - * values are sub-arrays. Each array contains the values of the header. - */ - public static function getHeadersAssoc() : array { - $retVal = []; - $headers = self::getHeaders(); - - foreach ($headers as $headerObj) { - if (!isset($retVal[$headerObj->getName()])) { - $retVal[$headerObj->getName()] = []; - } - $retVal[$headerObj->getName()][] = $headerObj->getValue(); - } - - return $retVal; + public function getMethod() : string { + return $this->getRequestMethod(); } /** - * Returns the pool which is used to hold request headers. + * Returns the value of GET or POST parameter. * - * @return HeadersPool - */ - public static function getHeadersPool() : HeadersPool { - return self::get()->headersPool; - } - /** - * Returns the name of request method which is used to call one of the services in the set. - * - * @return string Request method such as POST, GET, etc.... Default return - * value is 'GET'. The default is usually returned in case the call to - * this method was performed in CLI environment. To change request method - * in CLI environment to something like 'POST' for testing, use the - * function putenv('REQUEST_METHOD=POST'). + * @param string $paramName * + * @return string|null */ - public static function getMethod() : string { - $meth = getenv('REQUEST_METHOD'); - - if ($meth === false) { - $meth = $_SERVER['REQUEST_METHOD'] ?? ''; - } - $method = filter_var($meth, FILTER_SANITIZE_FULL_SPECIAL_CHARS); - - - if (!in_array($method, self::METHODS)) { - $method = 'GET'; - } - - return $method; + public function getParam(string $paramName) { + $params = $this->getParams(); + return $params[trim($paramName)] ?? null; } - /** - * Returns the value of a GET or POST parameter. - * - * This method will apply basic filtering to the value of the parameter before returning - * it. The developer may need to apply extra filtering to make sure that the - * value of the parameter is safe to use. - * - * @param string $paramName The name of the parameter. Note that if the value has extra - * spaces, they will be trimmed. - * - * @return string|null The method will return the value of the parameter if - * set as a string. Other than that, the method will return null. - * - */ - public static function getParam(string $paramName) { - $trimmed = trim($paramName); - $params = self::getParams(); - if (isset($params[$trimmed])) { - return $params[$trimmed]; - } - - return null; - } /** - * Returns the value of HTTP cookie given its name. + * Returns the value of a cookie. * - * @param string $cookieName The name of the cookie as it was sent from the client. + * @param string $cookieName * - * @return string|null If a cookie with given name exist on the request, - * its value is returned. Other than that, null is returned. + * @return string|null */ - public static function getCookieValue(string $cookieName) { + public function getCookieValue(string $cookieName) { $trimmedName = trim($cookieName); - return self::filter(INPUT_COOKIE, $trimmedName); + if (isset($_COOKIE[$trimmedName])) { + return filter_var($_COOKIE[$trimmedName]); + } + return null; } + /** - * Returns an array that contains all POST or GET parameters. + * Returns all request parameters. * - * @return array An array that contains all POST or GET parameters. The - * indices of the array are parameters names and the value of each index - * is the value of the parameter. + * @return array */ - public static function getParams() : array { - $requestMethod = self::getMethod(); + public function getParams() : array { + $method = $this->getMethod(); $retVal = []; - - if ($requestMethod == RequestMethod::POST || $requestMethod == RequestMethod::PUT || $requestMethod == RequestMethod::PATCH) { - foreach (array_keys($_POST) as $name) { - $retVal[$name] = self::filter(INPUT_POST, $name); + + if ($method == RequestMethod::GET) { + foreach ($_GET as $param => $val) { + $retVal[$param] = $this->filter(INPUT_GET, $param); } - } else if ($requestMethod == RequestMethod::DELETE || $requestMethod == RequestMethod::GET) { - foreach (array_keys($_GET) as $name) { - $retVal[$name] = self::filter(INPUT_GET, $name); + } else if ($method == RequestMethod::POST) { + foreach ($_POST as $param => $val) { + $retVal[$param] = $this->filter(INPUT_POST, $param); } } - + return $retVal; } + /** - * Returns path part of a requested URI. + * Returns the path part of request URI. * - * @return string Path part of a requested URI. + * @return string */ - public static function getPath() : string { - $path = self::getPathHelper('REQUEST_URI'); + public function getPath() : string { + $path = $this->getPathHelper('REQUEST_URI'); - if (strlen($path) == 0) { - $path = self::getPathHelper('X_ORIGINAL_URL'); - - if (strlen($path) == 0) { - $path = getenv('REQUEST_URI'); - - if ($path === false || strlen($path) == 0) { - $path = getenv('HTTP_REQUEST_URI'); - - if ($path === false || strlen($path) == 0) { - //Local dev server - $path = $_SERVER['PATH_INFO'] ?? ''; - } - } - } + if ($path === null) { + $path = $this->getPathHelper('PATH_INFO'); } - if (strlen($path) == 0) { - return '/'; + if ($path === null) { + $path = $this->getPathHelper('HTTP_REQUEST_URI'); } - return parse_url($path, PHP_URL_PATH); - } - private static function getPathHelper(string $header) { - $path = ''; - $headerVals = self::getHeader($header); + if ($path === null) { + $path = $this->getPathHelper('HTTP_X_ORIGINAL_URL'); + } - if (count($headerVals) != 0) { - $path = trim($headerVals[0]); + if ($path === null) { + $path = $this->getPathHelper('SCRIPT_NAME'); } - return $path; + if ($path === null) { + return '/'; + } + + return parse_url($path, PHP_URL_PATH); } + /** * Returns the URI of the requested resource. * - * @param string $pathToAppend If provided, this part will be - * appended to the final URI. It is useful in case of having multiple - * applications on same domain to have different paths. - * - * @return string The URI of the requested resource - * (e.g. http://example.com/get-random?range=[1,100]). + * @param string $pathToAppend * + * @return string */ - public static function getRequestedURI(string $pathToAppend = '') : string { - $base = Uri::getBaseURL(); + public function getRequestedURI(string $pathToAppend = '') : string { + $base = RequestUri::getBaseURL(); - $path = self::getPath(); - - if (strpos($path, '?') !== false) { - $split = explode('?', $path); - $path = $split[0]; - } - - $cleanedPath = str_replace(trim(str_replace('\\', '/', $pathToAppend), '/'),'' ,trim(filter_var($path),'/')); - $requestMethod = self::getMethod(); - $queryString = ''; - - - if ($requestMethod == RequestMethod::DELETE || $requestMethod == RequestMethod::GET) { - $getParams = Request::getParams(); + if (strlen($pathToAppend) != 0) { + $path = $this->getPath(); + $cleanPath = trim($pathToAppend, '/'); - if (count($getParams) != 0) { - $queryArr = []; - foreach ($getParams as $name => $val) { - $queryArr[] = $name.'='.$val; + if (strlen($cleanPath) != 0) { + if ($path[strlen($path) - 1] == '/') { + return $base.$path.$cleanPath; + } else { + return $base.$path.'/'.$cleanPath; } - $queryString = '?'.implode('&', $queryArr); } } - return $base.'/'.trim($cleanedPath, '/').$queryString; + + $uri = $base.$this->getPath(); + + if (!empty($_GET)) { + $uri .= '?'.http_build_query($_GET); + } + + return $uri; } + /** - * Returns an object that holds all information about requested URI. - * - * @return Uri an object that holds all information about requested URI. + * Returns an object of type 'Uri'. * + * @return RequestUri */ - public static function getUri() : Uri { - return new Uri(self::getRequestedURI()); + public function getUri() : RequestUri { + return new RequestUri($this->getRequestedURI()); + } + + private function getMethodFromGlobals() : string { + $meth = getenv('REQUEST_METHOD'); + + if ($meth === false) { + $meth = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : RequestMethod::GET; + } + + $method = filter_var($meth); + + if ($method !== false && in_array($method, RequestMethod::getAll())) { + return $method; + } + + return RequestMethod::GET; } - private static function extractHeaders() { - self::get()->headersPool = new HeadersPool(); + private function extractHeaders() { + $this->getHeadersPool()->clear(); + if (function_exists('apache_request_headers')) { $headers = apache_request_headers(); - + foreach ($headers as $k => $v) { - self::get()->headersPool->addHeader($k, filter_var($v, FILTER_SANITIZE_FULL_SPECIAL_CHARS)); + $this->addHeader($k, filter_var($v, FILTER_SANITIZE_FULL_SPECIAL_CHARS)); } - } - - if (isset($_SERVER)) { - $headersArr = self::getRequestHeadersFromServer(); - + } else { + $headersArr = $this->getRequestHeadersFromServer(); + foreach ($headersArr as $header) { - self::get()->headersPool->addHeader($header->getName(), $header->getValue(), null); + $this->addHeader($header->getName(), $header->getValue()); } } } - private static function filter($inputSource, $varName) { - $val = filter_input($inputSource, $varName); + private function filter($inputSource, $varName) { + $val = filter_input($inputSource, $varName); + if ($val === null) { if ($inputSource == INPUT_POST && isset($_POST[$varName])) { $val = filter_var(urldecode($_POST[$varName])); @@ -402,36 +277,39 @@ private static function filter($inputSource, $varName) { $val = filter_var(urldecode($_COOKIE[$varName])); } } - + return $val; } - /** - * Collect request headers from the array $_SERVER. - * @return array - */ - private static function getRequestHeadersFromServer() : array { - $retVal = []; - foreach ($_SERVER as $k => $v) { - $split = explode('_', $k); - - if ($split[0] == 'HTTP') { - $headerName = ''; - $count = count($split); + private function getPathHelper(string $header) { + $envVal = getenv($header); + if ($envVal !== false) { + return $envVal; + } + + if (isset($_SERVER[$header])) { + return $_SERVER[$header]; + } + + $headerVals = $this->getHeader($header); + + if (count($headerVals) == 1) { + return $headerVals[0]; + } + + return null; + } - for ($x = 0 ; $x < $count ; $x++) { - if ($x + 1 == $count && $split[$x] != 'HTTP') { - $headerName = $headerName.$split[$x]; - } else if ($x == 1 && $split[$x] != 'HTTP') { - $headerName = $split[$x].'-'; - } else if ($split[$x] != 'HTTP') { - $headerName = $headerName.$split[$x].'-'; - } - } - $retVal[] = new HttpHeader($headerName, filter_var($v, FILTER_SANITIZE_FULL_SPECIAL_CHARS)); + private function getRequestHeadersFromServer() : array { + $retVal = []; + + foreach ($_SERVER as $name => $value) { + if (substr($name, 0, 5) == 'HTTP_') { + $name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5))))); + $retVal[] = new HttpHeader($name, $value); } } - + return $retVal; } } diff --git a/WebFiori/Http/RequestParameter.php b/WebFiori/Http/RequestParameter.php index 0160c5bc..50837dfa 100644 --- a/WebFiori/Http/RequestParameter.php +++ b/WebFiori/Http/RequestParameter.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2019 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE diff --git a/WebFiori/Http/RequestUri.php b/WebFiori/Http/RequestUri.php new file mode 100644 index 00000000..cacb70de --- /dev/null +++ b/WebFiori/Http/RequestUri.php @@ -0,0 +1,382 @@ +getName(); + }, $this->getParameters()); + } + /** + * Returns the base URL of the framework. + * + * The returned value will depend on the folder where the library files + * are located. For example, if your domain is 'example.com' and the library + * is placed at the root and the requested resource is 'http://example.com/x/y/z', + * then the base URL will be 'http://example.com/'. If the library is + * placed inside a folder in the server which has the name 'system', and + * the same resource is requested, then the base URL will be + * 'http://example.com/system'. + * + * @return string The base URL (such as 'http//www.example.com/') + * + */ + public static function getBaseURL(?array $serverInputs = null) : string { + $server = $serverInputs ?? $_SERVER; + $tempHost = $server['HTTP_HOST'] ?? '127.0.0.1'; + $host = trim(filter_var($tempHost),'/'); + + if (isset($server['HTTPS'])) { + $secureHost = filter_var($server['HTTPS']); + } else { + $secureHost = ''; + } + $protocol = 'http://'; + $useHttp = defined('USE_HTTP') && USE_HTTP === true; + + if (strlen($secureHost) != 0 && !$useHttp) { + $protocol = "https://"; + } + + if (isset($server['DOCUMENT_ROOT'])) { + $docRoot = filter_var($server['DOCUMENT_ROOT']); + } else { + //Fix for IIS since the $_SERVER['DOCUMENT_ROOT'] is not set + //in some cases + $docRoot = getcwd(); + } + + $docRootLen = strlen($docRoot); + + if ($docRootLen == 0) { + $docRoot = __DIR__; + $docRootLen = strlen($docRoot); + } + + if (!defined('ROOT_PATH')) { + define('ROOT_PATH', __DIR__); + } + $toAppend = str_replace('\\', '/', substr(ROOT_PATH, $docRootLen, strlen(ROOT_PATH) - $docRootLen)); + + if (defined('WF_PATH_TO_REMOVE')) { + $toAppend = str_replace(str_replace('\\', '/', WF_PATH_TO_REMOVE),'' ,$toAppend); + } + $xToAppend = str_replace('\\', '/', $toAppend); + + if (defined('WF_PATH_TO_APPEND')) { + $xToAppend = $xToAppend.'/'.trim(str_replace('\\', '/', WF_PATH_TO_APPEND), '/'); + } + + if (strlen($xToAppend) == 0) { + return $protocol.$host; + } else { + return $protocol.$host.'/'.trim($xToAppend, '/'); + } + } + /** + * Returns the value of URI parameter given its name. + * + * A URI parameter is a string which is defined while creating the route. + * it is name is included between '{}'. + * + * @param string $varName The name of the parameter. Note that this value + * must not include braces. + * + * @return string|null The method will return the value of the + * parameter if found. If the parameter is not set or the parameter + * does not exist, the method will return null. + * + */ + public function getParameterValue(string $varName) : ?string { + $param = $this->getParameter($varName); + + if ($param !== null) { + return $param->getValue(); + } + + return null; + } + /** + * Creates new instance of the class. + * + * @param string $requestedUri The URI such as 'https://www3.programmingacademia.com:80/{some-var}/hell/{other-var}/?do=dnt&y=2018#xyz' + * + * @throws InvalidArgumentException + */ + public function __construct(string $requestedUri = '') { + parent::__construct($requestedUri); + $this->allowedMethods = []; + $this->vars = []; + $addedParams = []; + $pathArr = $this->getPathArray(); + + + foreach($pathArr as $part) { + $conv = mb_convert_encoding(urldecode($part), 'UTF-8', 'ISO-8859-1'); + + if ($conv[0] == '{' && $conv[strlen($conv) - 1] == '}') { + $name = trim($part, '{}'); + + if (!in_array($name, $addedParams)) { + $addedParams[] = $name; + $this->vars[] = new UriParameter($name); + } + } + + } + $this->verifyOrderOfParams(); + } + private function verifyOrderOfParams() { + $currentOptional = false; + + foreach($this->getParameters() as $param) { + if ($currentOptional == true && !$param->isOptional()) { + throw new \Exception('Requred paramater cannot appear after optional'); + } + $currentOptional = $param->isOptional() || $currentOptional; + } + } + + /** + * Adds new request method to the allowed methods. + * + * @param string $method The request method (e.g. 'GET', 'POST', 'PUT', etc...). + */ + public function addRequestMethod(string $method) : RequestUri { + $normalizedMethod = strtoupper(trim($method)); + + if (!in_array($normalizedMethod, $this->allowedMethods)) { + $this->allowedMethods[] = $normalizedMethod; + } + return $this; + } + + /** + * Adds a value to allowed URI parameter values. + * + * @param string $paramName The name of the parameter. + * @param string $value The value to add. + */ + public function addAllowedParameterValue(string $paramName, string $value) : RequestUri { + + $normalized = trim($paramName); + foreach ($this->getParameters() as $param) { + if ($param->getName() == $normalized) { + $param->addAllowedValue($value); + break; + } + } + return $this; + } + public function addAllowedParameterValues(string $name, array $vals) : RequestUri { + $normalizedName = trim($name); + + foreach ($this->getParameters() as $param) { + if ($param->getName() == $normalizedName) { + $param->addAllowedValues($vals); + break; + } + } + return $this; + } + + /** + * Checks if two URIs are equal or not. + * + * @param RequestUri $otherUri An object of type 'RequestUri'. + * + * @return bool The method will return true if the URIs are + * equal. False if not. + */ + public function equals(Uri $otherUri) : bool { + if (!parent::equals($otherUri)) { + return false; + } + $thisPath = $this->getPath(); + $otherPath = $otherUri->getPath(); + + $thisMethods = $this->getRequestMethods(); + $otherMethods = $otherUri->getRequestMethods(); + + if (count($thisMethods) != count($otherMethods)) { + return false; + } + + foreach ($thisMethods as $method) { + if (!in_array($method, $otherMethods)) { + return false; + } + } + + return true; + } + + /** + * Returns the value of URI parameter given its name. + * + * @param string $paramName The name of the parameter. + * + * @return UriParameter|null If a parameter which has the given name + * is found, it will be returned. If no such parameter, the method will + * return null. + */ + public function getParameter(string $paramName) : ?UriParameter { + + foreach ($this->getParameters() as $param) { + if ($param->getName() == $paramName) { + return $param; + } + } + + return null; + } + + /** + * Returns an array that contains all URI parameters. + * + * @return array An array that contains objects of type 'UriParameter'. + */ + public function getParameters() : array { + return $this->vars; + } + + /** + * Returns an array that contains allowed URI parameters values. + * + * @return array + */ + public function getAllowedParameterValues(string $varName) : array { + + $param = $this->getParameter($varName); + if ($param !== null) { + + return $param->getAllowedValues(); + } + + return []; + } + + /** + * Returns an array that contains all allowed request methods. + * + * @return array An array that contains all allowed request methods. + */ + public function getRequestMethods() : array { + return $this->allowedMethods; + } + + /** + * Checks if the URI has any parameters or not. + * + * @return bool The method will return true if the URI has any parameters. + * false if not. + */ + public function hasParameters() : bool { + return count($this->getParameters()) > 0; + } + + /** + * Checks if the URI has a specific parameter or not. + * + * @param string $paramName The name of the parameter. + * + * @return bool The method will return true if the parameter + * is found. false if not. + */ + public function hasParameter(string $paramName) : bool { + return $this->getParameter($paramName) !== null; + } + + /** + * Checks if all URI parameters have values or not. + * + * @return bool The method will return true if all URI parameters + * have values. false if not. + */ + public function isAllParametersSet() : bool { + $uriVars = $this->getParameters(); + + foreach ($uriVars as $param) { + if ($param->getValue() === null && !$param->isOptional()) { + return false; + } + } + + return true; + } + + /** + * Checks if a request method is allowed or not. + * + * @param string $method The request method (e.g. 'GET', 'POST', 'PUT', etc...). + * If not provided, the method will attempt to get request method using the environment + * variable 'REQUEST_METHOD'. + * + * @return bool The method will return true if the method is allowed. + * If no request methods are specified, the method will return true. + * Other than that, the method will return false. + */ + public function isRequestMethodAllowed(?string $method = null) : bool { + if ($method === null) { + $method = getenv('REQUEST_METHOD'); + if (!in_array($method, RequestMethod::getAll())) { + return false; + } + } + $normalizedMethod = strtoupper(trim($method)); + $methods = $this->getRequestMethods(); + + return count($methods) == 0 || in_array($normalizedMethod, $methods); + } + + /** + * Sets the value of a URI parameter. + * + * @param string $paramName The name of the parameter. + * @param string $value The value to set. + */ + public function setParameterValue(string $paramName, string $value) : bool { + $param = $this->getParameter($paramName); + + if ($param !== null) { + return $param->setValue($value); + } + return false; + } + + /** + * Sets the array of allowed request methods. + * + * @param array $methods An array that contains request methods names. + */ + public function setRequestMethods(array $methods) : RequestUri { + $this->allowedMethods = []; + + foreach ($methods as $method) { + $this->addRequestMethod($method); + } + return $this; + } +} diff --git a/WebFiori/Http/Response.php b/WebFiori/Http/Response.php index 204f393a..22160469 100644 --- a/WebFiori/Http/Response.php +++ b/WebFiori/Http/Response.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2019 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE diff --git a/WebFiori/Http/Uri.php b/WebFiori/Http/Uri.php index 61390740..80f35e34 100644 --- a/WebFiori/Http/Uri.php +++ b/WebFiori/Http/Uri.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2019 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE @@ -10,171 +10,98 @@ namespace WebFiori\Http; use InvalidArgumentException; + /** - * A class that is used to split URIs and get their parameters. - * - * The main aim of this class is to extract URI parameters including: - * - * For more information on URI structure, visit Wikipedia. - * + * A class for representing and parsing URIs. + * * @author Ibrahim - * */ class Uri { /** - * - * @var array + * An array that contains URI parts. * - */ - private $allowedRequestMethods; - /** - * The URI broken into its subcomponents (scheme, authority ...) as an associative - * array. - * @var array + * @var array */ private $uriBroken; + /** - * Creates new instance. + * Creates new instance of the class. * - * @param string $requestedUri The URI such as 'https://www3.webfiori.com:80/{some-var}/hell/{other-var}/?do=dnt&y=#xyz' - */ - public function __construct(string $requestedUri) { - $this->allowedRequestMethods = []; - $this->uriBroken = self::splitURI($requestedUri); - - if (gettype($this->uriBroken) != 'array') { - throw new InvalidArgumentException('Invalid URI given.'); - } - - if (!$this->checkOptionalParamsOrder()) { - throw new InvalidArgumentException('Incorrect parameters order.'); - } - $this->uriBroken['vars-possible-values'] = []; - - foreach ($this->getParametersNames() as $varName) { - $this->uriBroken['vars-possible-values'][$varName] = []; - } - } - - /** - * Adds a request method to the allowed set of methods at which the URI can - * be called with. - * - * @param string $method A string such as 'GET' or 'POST'. Note that the - * value must exist in the array Request::METHODS, or it will be not added. + * @param string $requestedUri The URI such as 'https://www3.programmingacademia.com:80/{some-var}/hell/{other-var}/?do=dnt&y=2018#xyz' * + * @throws InvalidArgumentException */ - public function addRequestMethod(string $method) { - if (in_array($method, Request::METHODS)) { - $this->allowedRequestMethods[] = $method; - } - } - /** - * Adds a possible value for a URI parameter. - * - * This is used in constructing the sitemap node of the URI. If a value is - * provided, then it will be part of the URI that will appear in the sitemap. - * - * @param string $varName The name of the parameter. It must be existed as the path part in the URI. - * - * @param string $varValue The value of the parameter. Note that any extra spaces - * in the value will be trimmed. - * - */ - public function addVarValue(string $varName, string $varValue) { - $trimmed = trim($varName); - $trimmedVal = trim($varValue); - - if (strlen($trimmedVal) != 0 - && isset($this->uriBroken['vars-possible-values'][$trimmed]) - && !in_array($trimmedVal, $this->uriBroken['vars-possible-values'][$trimmed])) { - $this->uriBroken['vars-possible-values'][$trimmed][] = $trimmedVal; - } - } - /** - * Adds multiple values to URI parameter. - * - * @param string $varName The name of the parameter. - * - * @param array $arrayOfValues An array that contains all possible values for - * the parameter. - * - */ - public function addVarValues(string $varName, array $arrayOfValues) { - if (gettype($arrayOfValues) == 'array') { - foreach ($arrayOfValues as $val) { - $this->addVarValue($varName, $val); + public function __construct(string $requestedUri = '') { + if (strlen(trim($requestedUri)) == 0) { + throw new InvalidArgumentException('URI must be non-empty string'); + } else { + $this->uriBroken = self::splitURI($requestedUri); + + if ($this->uriBroken === false) { + throw new InvalidArgumentException('Invalid URI: \''.$requestedUri.'\''); } } } /** - * Checks if two URIs are equal or not. + * Returns the original requested URI. * - * Two URIs are considered equal if they have the same authority and the - * same path name. + * @param boolean $incQueryStr If set to true, the query string part + * will be included in the URL. Default is false. * - * @param Uri $otherUri The URI which 'this' URI will be checked against. + * @param boolean $incFragment If set to true, the fragment part + * will be included in the URL. Default is false. * - * @return boolean The method will return true if the URIs are - * equal. + * @return string The original requested URI. * */ - public function equals(Uri $otherUri) : bool { - $isEqual = true; + public function getUri(bool $incQueryStr = false, bool $incFragment = false) : string { + $retVal = $this->getScheme().':'.$this->getAuthority().$this->getPath(); - if ($this->getAuthority() == $otherUri->getAuthority()) { - $thisPathNames = $this->getPathArray(); - $otherPathNames = $otherUri->getPathArray(); - $booleansArr = []; + if ($incQueryStr === true && $incFragment === true) { + $queryStr = $this->getQueryString(); - foreach ($thisPathNames as $path1) { - $booleansArr[] = in_array($path1, $otherPathNames); + if (strlen($queryStr) != 0) { + $retVal .= '?'.$queryStr; } + $fragment = $this->getFragment(); - foreach ($otherPathNames as $path) { - $booleansArr[] = in_array($path, $thisPathNames); + if (strlen($fragment) != 0) { + $retVal .= '#'.$fragment; } + } else { + if ($incQueryStr === true && $incFragment === false) { + $queryStr = $this->getQueryString(); - foreach ($booleansArr as $bool) { - $isEqual = $isEqual && $bool; - } + if (strlen($queryStr) != 0) { + $retVal .= '?'.$queryStr; + } + } else { + if ($incQueryStr === false && $incFragment === true) { + $fragment = $this->getFragment(); - return $isEqual; + if (strlen($fragment) != 0) { + $retVal .= '#'.$fragment; + } + } + } } - return false; + return $retVal; } /** - * Returns authority part of the URI. + * Returns the authority part of the URI. * * @return string The authority part of the URI. Usually, - * it is a string in the form '//www.example.com:80'. - * + * it is the sub-domain + domain name + port number. */ public function getAuthority() : string { return $this->uriBroken['authority']; } + /** * Returns the base URL of the framework. * - * The returned value will depend on the folder where the library files - * are located. For example, if your domain is 'example.com' and the library - * is placed at the root and the requested resource is 'http://example.com/x/y/z', - * then the base URL will be 'http://example.com/'. If the library is - * placed inside a folder in the server which has the name 'system', and - * the same resource is requested, then the base URL will be - * 'http://example.com/system'. - * - * @return string The base URL (such as 'http//www.example.com/') - * + * @return string The base URL (such as 'https://example.com/' or 'http://127.0.0.1/') */ public static function getBaseURL() : string { $tempHost = $_SERVER['HTTP_HOST'] ?? '127.0.0.1'; @@ -227,8 +154,9 @@ public static function getBaseURL() : string { return $protocol.$host.'/'.trim($xToAppend, '/'); } } + /** - * Returns an associative array which contains all URI parts. + * Returns an array that contains all URI parts. * * @return array The method will return an associative array that * contains the components of the URI. The array will have the @@ -236,360 +164,153 @@ public static function getBaseURL() : string { * - * */ public function getComponents() : array { return $this->uriBroken; } + /** - * Returns fragment part of the URI. + * Returns the fragment part of the URI. * - * @return string Fragment part of the URI. The fragment in the URI is + * @return string The fragment part of the URI. The fragment in the URI is * any string that comes after the character '#'. - * */ public function getFragment() : string { return $this->uriBroken['fragment']; } + /** - * Returns host name from the host part of the URI. - * - * @return string The host name such as 'www.webfiori.com'. + * Returns the host name from the authority part of the URI. * + * @return string The host name such as 'www.example.com'. */ public function getHost() : string { return $this->uriBroken['host']; } /** - * Returns URI parameter given its name. - * - * @param string $name The name of the parameter. - * - * @return UriParameter|null If such parameter is found, it will be returned - * as an object. Other than that, null is returned. - */ - public function getParameter(string $name) { - foreach ($this->getParameters() as $obj) { - if ($obj->getName() == $name) { - return $obj; - } - } - - return null; - } - /** - * Returns an array which contains URI parameters as objects. - * - * @return array An indexed array which contains URI parameters as - * objects of type UriParameter. - * - */ - public function getParameters() : array { - return $this->uriBroken['uri-vars']; - } - public function getParametersNames() : array { - $retVal = []; - - foreach ($this->getParameters() as $paramObj) { - $retVal[] = $paramObj->getName(); - } - - return $retVal; - } - /** - * Returns the value of URI parameter given its name. - * - * A URI parameter is a string which is defined while creating the route. - * it is name is included between '{}'. - * - * @param string $varName The name of the parameter. Note that this value - * must not include braces. - * - * @return string|null The method will return the value of the - * parameter if found. If the parameter is not set or the parameter - * does not exist, the method will return null. - * - */ - public function getParameterValue(string $varName) { - $param = $this->getParameter($varName); - - if ($param !== null) { - return $param->getValue(); - } - - return null; - } - /** - * Returns an array that contains possible values for a URI parameter. - * - * @param string $varName The name of the parameter. + * Returns an array which contains the names of URI directories. * - * @return array The method will return an array that contains all possible - * values for the parameter which was added using the method Router::addUriVarValue(). - * If the parameter does not exist, the array will be empty. + * @return array An array which contains the names of URI directories. + * For example, if the path part of the URI is '/path1/path2', the + * array will contain the value 'path1' at index 0 and 'path2' at index 1. * */ - public function getParameterValues(string $varName) : array { - $trimmed = trim($varName); - - if (isset($this->uriBroken['vars-possible-values'][$trimmed])) { - return $this->uriBroken['vars-possible-values'][$trimmed]; - } - - return []; + public function getPathArray() : array { + return $this->uriBroken['path']; } /** * Returns the path part of the URI. * * @return string A string such as '/path1/path2/path3'. - * */ public function getPath() : string { - $retVal = ''; - - foreach ($this->uriBroken['path'] as $dir) { - $retVal .= '/'.$dir; + $path = $this->uriBroken['path']; + + if (count($path) == 0) { + return '/'; } - - return $retVal; - } - /** - * Returns an array which contains the names of URI directories. - * - * @return array An array which contains the names of URI directories. - * For example, if the path part of the URI is '/path1/path2', the - * array will contain the value 'path1' at index 0 and 'path2' at index 1. - * - */ - public function getPathArray() : array { - return $this->uriBroken['path']; + + return '/'.implode('/', $path); } + /** - * Returns port number of the authority part of the URI. - * - * @return string Port number of the authority part of the URI. If - * port number was not specified, the method will return empty string. + * Returns the port number of the authority part of the URI. * + * @return string The port number such as '80' or '443'. If no port + * number is specified, the method will return empty string. */ public function getPort() : string { return $this->uriBroken['port']; } + /** * Returns the query string that was appended to the URI. * - * @return string The query string that was appended to the URI. - * If the URI has no query string, the method will return empty - * string. - * + * @return string The query string such as 'do=dnt&y=2018'. If the URI + * has no query string, the method will return empty string. */ public function getQueryString() : string { return $this->uriBroken['query-string']; } + /** * Returns an associative array which contains query string parameters. * - * @return array An associative array which contains query string parameters. - * the keys will be acting as the names of the parameters and the values - * of each parameter will be in its key. - * + * @return array An associative array. The keys will be parameters + * names and the values are parameters values. If the URI has no query + * string, the method will return empty array. */ public function getQueryStringVars() : array { return $this->uriBroken['query-string-vars']; } - /** - * Returns an array that holds all allowed request methods at which the - * URI can be called with. - * - * @return array An array that holds strings such as 'GET' or 'POST'. - * - */ - public function getRequestMethods() : array { - return $this->allowedRequestMethods; - } + /** * Returns the scheme part of the URI. * - * @return string The scheme part of the URI. Usually, it is called protocol - * (like http, ftp). - * + * @return string The scheme part of the URI. Usually, it can be + * 'http' or 'https'. */ public function getScheme() : string { return $this->uriBroken['scheme']; } + /** - * Returns the original requested URI. + * Splits a string based on character mask. * - * @param boolean $incQueryStr If set to true, the query string part - * will be included in the URL. Default is false. + * @param string $split The string to split. * - * @param boolean $incFragment If set to true, the fragment part - * will be included in the URL. Default is false. + * @param string $char The character that the split is based on. * - * @return string The original requested URI. + * @param string $encoded The character when encoded in URI. * + * @return array */ - public function getUri(bool $incQueryStr = false, bool $incFragment = false) : string { - $retVal = $this->getScheme().':'.$this->getAuthority().$this->getPath(); - - if ($incQueryStr === true && $incFragment === true) { - $queryStr = $this->getQueryString(); + private static function _queryOrFragment(string $split, string $char, string $encoded) : array { + $split2 = explode($char, $split); + $spCount = count($split2); - if (strlen($queryStr) != 0) { - $retVal .= '?'.$queryStr; - } - $fragment = $this->getFragment(); + if ($spCount > 2) { + $temp = []; - if (strlen($fragment) != 0) { - $retVal .= '#'.$fragment; + for ($x = 0 ; $x < $spCount - 1 ; $x++) { + $temp[] = $split2[$x]; } - } else { - if ($incQueryStr === true && $incFragment === false) { - $queryStr = $this->getQueryString(); + $lastStr = $split2[$spCount - 1]; - if (strlen($queryStr) != 0) { - $retVal .= '?'.$queryStr; - } + if (strlen($lastStr) == 0) { + $split2 = [ + implode($encoded, $temp).$encoded + ]; } else { - if ($incQueryStr === false && $incFragment === true) { - $fragment = $this->getFragment(); - - if (strlen($fragment) != 0) { - $retVal .= '#'.$fragment; - } - } + $split2 = [ + implode($encoded, $temp), + $split2[$spCount - 1] + ]; } } - return $retVal; - } - /** - * Checks if the URI has a parameter or not given its name. - * - * A parameter is a string which is defined while creating the route. - * it is name is included between '{}'. - * - * @param string $varName The name of the parameter. - * - * @return boolean If the given parameter name is exist, the method will - * return true. Other than that, the method will return false. - * - */ - public function hasParameter(string $varName) : bool { - return in_array($varName, $this->getParametersNames()); - } - /** - * Checks if the URI has any parameters or not. - * - * A parameter is a string which is defined while creating the route. - * it is name is included between '{}'. - * - * @return bool If the URI has any parameters, the method will - * return true. - * - */ - public function hasParameters() : bool { - return count($this->getParameters()) != 0; - } - /** - * Checks if all URI parameters has values or not. - * - * @return bool The method will return true if all non-optional URI - * parameters have a value other than null. - * - */ - public function isAllParametersSet() : bool { - $canRoute = true; - - foreach ($this->getParameters() as $val) { - $canRoute = $canRoute && ($val->isOptional() || $val->getValue() != null); - } - - return $canRoute; - } - - /** - * Checks if URI is fetched using allowed request method or not. - * - * @return boolean The method will return true in two cases, if the array - * that holds allowed request methods is empty or request method is existed - * in the allowed request methods. Other than that, the method will return - * false. - * - */ - public function isRequestMethodAllowed() : bool { - $methods = $this->getRequestMethods(); - - return count($methods) == 0 || in_array(Request::getMethod(), $this->getRequestMethods()); - } - - /** - * Sets the value of a URI parameter. - * - * A parameter is a string which is defined while creating the route. - * it is name is included between '{}'. - * - * @param string $varName The name of the parameter. - * - * @param string $value The value of the parameter. - * - * @return bool The method will return true if the parameter - * was set. If the parameter does not exist, the method will return false. - * - */ - public function setParameterValue(string $varName, string $value) : bool { - $param = $this->getParameter($varName); - - if ($param !== null) { - $param->setValue($value); - - return true; - } - - return false; + return $split2; } - /** - * Adds a set of request methods to the allowed methods at which the URI - * can be called with. - * - * @param array $methods An array that holds strings such as 'GET' or 'POST'. - * - */ - public function setRequestMethods(array $methods) { - foreach ($methods as $m) { - $this->addRequestMethod($m); - } + public function equals(Uri $uri) : bool { + return $this->getUri(true, true) == $uri->getUri(true, true); } /** - * Breaks a URI into its basic components. + * Splits a URI into its basic components. * - * @param string $uri The URI that will be broken. - * - * @return array|boolean If the given URI is not valid, - * the Method will return false. Other than that, The method will return an associative array that - * contains the components of the URI. The array will have the - * following indices: - * + * @param string $uri The URI that will be split. * + * @return array|bool If the given URI is not valid, + * the method will return false. Other than that, The method will return an associative array that + * contains the components of the URI. */ public static function splitURI(string $uri) { $validate = filter_var(str_replace(' ', '%20', $uri),FILTER_VALIDATE_URL); @@ -649,15 +370,6 @@ public static function splitURI(string $uri) { if ($dirName != '') { $retVal['path'][] = mb_convert_encoding(urldecode($dirName), 'UTF-8', 'ISO-8859-1'); - - if ($dirName[0] == '{' && $dirName[strlen($dirName) - 1] == '}') { - $name = trim($split4[$x], '{}'); - - if (!in_array($name, $addedParams)) { - $addedParams[] = $name; - $retVal['uri-vars'][] = new UriParameter($name); - } - } } } //now extract port number from the authority (if any) @@ -675,55 +387,4 @@ public static function splitURI(string $uri) { return $retVal; } - - /** - * Splits a string based on character mask. - * - * @param string $split The string to split. - * - * @param string $char The character that the split is based on. - * - * @param string $encoded The character when encoded in URI. - * - * @return array - */ - private static function _queryOrFragment(string $split, string $char, string $encoded) : array { - $split2 = explode($char, $split); - $spCount = count($split2); - - if ($spCount > 2) { - $temp = []; - - for ($x = 0 ; $x < $spCount - 1 ; $x++) { - $temp[] = $split2[$x]; - } - $lastStr = $split2[$spCount - 1]; - - if (strlen($lastStr) == 0) { - $split2 = [ - implode($encoded, $temp).$encoded - ]; - } else { - $split2 = [ - implode($encoded, $temp), - $split2[$spCount - 1] - ]; - } - } - - return $split2; - } - private function checkOptionalParamsOrder() : bool { - $hasOptional = false; - - foreach ($this->getParameters() as $obj) { - $hasOptional = $hasOptional || $obj->isOptional(); - - if ($hasOptional && !$obj->isOptional()) { - return false; - } - } - - return true; - } } diff --git a/WebFiori/Http/UriParameter.php b/WebFiori/Http/UriParameter.php index 8e27fb2f..b551ef4d 100644 --- a/WebFiori/Http/UriParameter.php +++ b/WebFiori/Http/UriParameter.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2019 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE @@ -30,6 +30,7 @@ class UriParameter { * @var string */ private $value; + private $allowedValues; /** * Creates new instance of the class. * @@ -56,6 +57,24 @@ public function __construct(string $varName) { $this->isOptional = false; } $this->name = trim($trimmed, '?'); + $this->allowedValues = []; + } + public function addAllowedValues(array $vals) : UriParameter { + foreach ($vals as $val) { + $this->addAllowedValue($val); + } + return $this; + } + public function addAllowedValue(string $val) : UriParameter { + $this->allowedValues[] = trim($val); + $currentVal = $this->getValue(); + if ($currentVal !== null && !in_array($currentVal, $this->allowedValues)) { + $this->value = null; + } + return $this; + } + public function getAllowedValues() : array { + return $this->allowedValues; } /** * Returns the name of the parameter. @@ -71,7 +90,7 @@ public function getName() : string { * @return string|null If the value of the parameter is set, it will * be returned as string. If not set, null is returned. */ - public function getValue() { + public function getValue() : ?string { return $this->value; } /** @@ -85,9 +104,21 @@ public function isOptional() : bool { /** * Sets the value of the parameter. * + * Note that if the parameter has a set of allowed values, the method + * will only accept the value if its part if that set. + * * @param string $val The value of the parameter as string. */ - public function setValue(string $val) { - $this->value = $val; + public function setValue(string $val) : bool { + $allowed = $this->getAllowedValues(); + $trimmed = trim($val); + if (count($allowed) > 0 && !in_array($trimmed, $allowed)) { + return false; + } + if ($trimmed != '') { + $this->value = $trimmed; + return true; + } + return false; } } diff --git a/WebFiori/Http/WebServicesManager.php b/WebFiori/Http/WebServicesManager.php index bd08008b..7315dd17 100644 --- a/WebFiori/Http/WebServicesManager.php +++ b/WebFiori/Http/WebServicesManager.php @@ -2,7 +2,7 @@ /** * This file is licensed under MIT License. * - * Copyright (c) 2019 Ibrahim BinAlshikh + * Copyright (c) 2019 WebFiori Framework * * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE @@ -24,16 +24,6 @@ * */ class WebServicesManager implements JsonI { - /** - * A constant which is used to indicate that the message that will be - * sent is of type error - */ - const E = 'error'; - /** - * A constant which is used to indicate that the message that will be - * sent is of type info - */ - const I = 'info'; /** * An array that contains the supported 'POST' request content types. * @@ -106,6 +96,7 @@ class WebServicesManager implements JsonI { * */ private $services; + private $request; /** * Creates new instance of the class. * @@ -124,7 +115,7 @@ class WebServicesManager implements JsonI { * number must follow the format 'X.X.X' where 'X' is a number between * 0 and 9 inclusive. */ - public function __construct(string $version = '1.0.0') { + public function __construct(?Request $request = null, string $version = '1.0.0') { $this->setVersion($version); $this->setDescription('NO DESCRIPTION'); @@ -132,6 +123,11 @@ public function __construct(string $version = '1.0.0') { $this->services = []; $this->invParamsArr = []; $this->missingParamsArr = []; + $this->request = $request ?? Request::createFromGlobals(); + } + public function setRequest(Request $request) : WebServicesManager { + $this->request = $request; + return $this; } /** * Adds new web service to the set of web services. @@ -140,8 +136,8 @@ public function __construct(string $version = '1.0.0') { * * */ - public function addService(AbstractWebService $service) { - $this->addAction($service); + public function addService(AbstractWebService $service) : WebServicesManager { + return $this->addAction($service); } /** * Sends a response message to indicate that request content type is @@ -164,7 +160,7 @@ public function addService(AbstractWebService $service) { public function contentTypeNotSupported(string $cType = '') { $j = new Json(); $j->add('request-content-type', $cType); - $this->sendResponse(ResponseMessage::get('415'), self::E, 415,$j); + $this->sendResponse(ResponseMessage::get('415'), 415, AbstractWebService::E, $j); } /** * Returns the name of the service which is being called. @@ -331,10 +327,13 @@ public function invParams() { } $i++; } - $this->sendResponse(ResponseMessage::get('404-1').$val.'.', self::E, 404, new Json([ + $this->sendResponse(ResponseMessage::get('404-1').$val.'.', 404, AbstractWebService::E, new Json([ 'invalid' => $paramsNamesArr ])); } + public function getRequest() : Request { + return $this->request; + } /** * Checks if request content type is supported by the service or not (For 'POST' * and PUT requests only). @@ -346,8 +345,8 @@ public function invParams() { * */ public final function isContentTypeSupported() : bool { - $c = Request::getContentType(); - $rm = Request::getMethod(); + $c = $this->getRequest()->getContentType(); + $rm = $this->getRequest()->getMethod(); if ($c !== null && ($rm == RequestMethod::POST || $rm == RequestMethod::PUT)) { return in_array($c, self::POST_CONTENT_TYPES); @@ -384,7 +383,7 @@ public function missingParams() { } $i++; } - $this->sendResponse(ResponseMessage::get('404-2').$val.'.', self::E, 404, new Json([ + $this->sendResponse(ResponseMessage::get('404-2').$val.'.', 404, AbstractWebService::E, new Json([ 'missing' => $paramsNamesArr ])); } @@ -403,7 +402,7 @@ public function missingParams() { * */ public function missingServiceName() { - $this->sendResponse(ResponseMessage::get('404-3'), self::E, 404); + $this->sendResponse(ResponseMessage::get('404-3'), 404, AbstractWebService::E); } /** * Sends a response message to indicate that a user is not authorized call a @@ -420,7 +419,7 @@ public function missingServiceName() { * */ public function notAuth() { - $this->sendResponse(ResponseMessage::get('401'), self::E, 401); + $this->sendResponse(ResponseMessage::get('401'), 401, AbstractWebService::E); } /** @@ -455,7 +454,7 @@ public final function process() { } } } else { - $c = Request::getContentType(); + $c = $this->getRequest()->getContentType(); if ($c === null) { $c = 'NOT_SET'; @@ -528,7 +527,7 @@ public function removeServices() { * */ public function requestMethodNotAllowed() { - $this->sendResponse(ResponseMessage::get('405'), self::E, 405); + $this->sendResponse(ResponseMessage::get('405'), 405, AbstractWebService::E); } /** * Sends Back a data using specific content type and specific response code. @@ -592,7 +591,7 @@ public function sendHeaders(array $headersArr) { * will be not included in response. Default is empty string. Default is null. * */ - public function sendResponse(string $message, string $type = '', int $code = 200, mixed $otherInfo = '') { + public function sendResponse(string $message, int $code = 200, string $type = '', mixed $otherInfo = '') { $json = new Json(); $json->add('message', $message); $typeTrimmed = trim($type); @@ -632,7 +631,7 @@ public function sendResponse(string $message, string $type = '', int $code = 200 * */ public function serviceNotImplemented() { - $this->sendResponse(ResponseMessage::get('404-4'), self::E, 404); + $this->sendResponse(ResponseMessage::get('404-4'), 404, AbstractWebService::E); } /** * Sends a response message to indicate that called web service is not supported by the API. @@ -648,7 +647,7 @@ public function serviceNotImplemented() { * */ public function serviceNotSupported() { - $this->sendResponse(ResponseMessage::get('404-5'), self::E, 404); + $this->sendResponse(ResponseMessage::get('404-5'), 404, AbstractWebService::E); } /** * Sets the description of the web services set. @@ -821,7 +820,7 @@ private function _checkAction(): bool { $allowedMethods = $calledService->getRequestMethods(); if (count($allowedMethods) != 0) { - $isValidRequestMethod = in_array(Request::getMethod(), $allowedMethods); + $isValidRequestMethod = in_array($this->getRequest()->getMethod(), $allowedMethods); if (!$isValidRequestMethod) { $this->requestMethodNotAllowed(); @@ -829,7 +828,7 @@ private function _checkAction(): bool { return true; } } else { - $this->sendResponse(ResponseMessage::get('404-6'), self::E, 404); + $this->sendResponse(ResponseMessage::get('404-6'), 404, AbstractWebService::E); } } else { $this->serviceNotSupported(); @@ -883,17 +882,18 @@ private function _processNonJson($params) { * * @deprecated since version 1.4.7 Use WebservicesSet::addService() */ - private function addAction(AbstractWebService $service) { + private function addAction(AbstractWebService $service) : WebServicesManager { $this->services[$service->getName()] = $service; $service->setManager($this); + return $this; } /** * @throws Exception */ private function filterInputsHelper() { - $reqMeth = Request::getMethod(); - $contentType = Request::getContentType(); + $reqMeth = $this->getRequest()->getMethod(); + $contentType = $this->getRequest()->getContentType(); if ($reqMeth == RequestMethod::GET || $reqMeth == RequestMethod::DELETE || @@ -919,11 +919,11 @@ private function filterInputsHelper() { * @deprecated since version 1.4.6 Use WebServicesManager::getCalledServiceName() instead. */ private function getAction() { - $reqMeth = Request::getMethod(); + $reqMeth = $this->getRequest()->getMethod(); $serviceIdx = ['action','service', 'service-name']; - $contentType = Request::getContentType(); + $contentType = $this->getRequest()->getContentType(); $retVal = null; if ($contentType == 'application/json') { @@ -963,7 +963,7 @@ private function getAction() { } private function isAuth(AbstractWebService $service) { if ($service->isAuthRequired()) { - $isAuthCheck = 'isAuthorized'.Request::getMethod(); + $isAuthCheck = 'isAuthorized'.$this->getRequest()->getMethod(); if (!method_exists($service, $isAuthCheck)) { return $service->isAuthorized() === null || $service->isAuthorized(); @@ -975,7 +975,7 @@ private function isAuth(AbstractWebService $service) { return true; } private function processService(AbstractWebService $service) { - $processMethod = 'process'.Request::getMethod(); + $processMethod = 'process'.$this->getRequest()->getMethod(); if (!method_exists($service, $processMethod)) { $service->processRequest(); diff --git a/composer.json b/composer.json index 5ab400fb..fbc4a390 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ }, "scripts": { "test": "phpunit --configuration tests/phpunit.xml", - "test-10": "phpunit --configuration tests/phpunit10.xml" + "test10": "phpunit --configuration tests/phpunit10.xml" }, "require-dev": { "phpunit/phpunit": "^10.0" diff --git a/tests/WebFiori/Tests/Http/HttpCookieTest.php b/tests/WebFiori/Tests/Http/HttpCookieTest.php index 518b1129..4e8402a8 100644 --- a/tests/WebFiori/Tests/Http/HttpCookieTest.php +++ b/tests/WebFiori/Tests/Http/HttpCookieTest.php @@ -70,12 +70,12 @@ public function testConstructor03() { $this->assertEquals('/', $cookie->getPath()); $this->assertEquals('', $cookie->getLifetime()); $this->assertEquals('Lax', $cookie->getSameSite()); - $this->assertEquals('127.0.0.1', $cookie->getDomain()); + $this->assertNull($cookie->getDomain()); $cookie->setValue('super'); $this->assertEquals('super', $cookie->getValue()); $cookie->setIsHttpOnly(false); $this->assertFalse($cookie->isHttpOnly()); - $this->assertEquals('new-cookie=super; domain=127.0.0.1; path=/; Secure; SameSite=Lax', $cookie->getHeaderString()); + $this->assertEquals('new-cookie=super; path=/; Secure; SameSite=Lax', $cookie->getHeaderString()); $cookie->setIsSecure(false); $this->assertFalse($cookie->isSecure()); $cookie->setDomain(); diff --git a/tests/WebFiori/Tests/Http/HttpMessageTest.php b/tests/WebFiori/Tests/Http/HttpMessageTest.php new file mode 100644 index 00000000..1ee91cdd --- /dev/null +++ b/tests/WebFiori/Tests/Http/HttpMessageTest.php @@ -0,0 +1,184 @@ +message = new HttpMessage(); + } + + /** + * @test + */ + public function testConstructor() { + $message = new HttpMessage(); + $this->assertInstanceOf(HeadersPool::class, $message->getHeadersPool()); + } + + /** + * @test + */ + public function testGetHeadersPool() { + $pool = $this->message->getHeadersPool(); + $this->assertInstanceOf(HeadersPool::class, $pool); + } + + /** + * @test + */ + public function testAddHeader() { + $result = $this->message->addHeader('Content-Type', 'application/json'); + $this->assertTrue($result); + + $headers = $this->message->getHeaders(); + $this->assertCount(1, $headers); + $this->assertEquals('content-type', $headers[0]->getName()); + $this->assertEquals('application/json', $headers[0]->getValue()); + } + + /** + * @test + */ + public function testAddInvalidHeader() { + $result = $this->message->addHeader('Invalid Header Name', 'value'); + $this->assertFalse($result); + } + + /** + * @test + */ + public function testGetHeader() { + $this->message->addHeader('Content-Type', 'application/json'); + + $values = $this->message->getHeader('content-type'); + $this->assertEquals(['application/json'], $values); + + $empty = $this->message->getHeader('non-existent'); + $this->assertEquals([], $empty); + } + + /** + * @test + */ + public function testGetHeaders() { + $this->assertEquals([], $this->message->getHeaders()); + + $this->message->addHeader('Content-Type', 'application/json'); + $this->message->addHeader('Accept', 'text/html'); + + $headers = $this->message->getHeaders(); + $this->assertCount(2, $headers); + } + + /** + * @test + */ + public function testHasHeader() { + $this->assertFalse($this->message->hasHeader('Content-Type')); + + $this->message->addHeader('Content-Type', 'application/json'); + + $this->assertTrue($this->message->hasHeader('Content-Type')); + $this->assertTrue($this->message->hasHeader('content-type')); + $this->assertTrue($this->message->hasHeader('Content-Type', 'application/json')); + $this->assertFalse($this->message->hasHeader('Content-Type', 'text/html')); + } + + /** + * @test + */ + public function testRemoveHeader() { + $this->message->addHeader('Content-Type', 'application/json'); + $this->message->addHeader('Accept', 'text/html'); + + $this->assertTrue($this->message->hasHeader('Content-Type')); + + $result = $this->message->removeHeader('Content-Type'); + $this->assertTrue($result); + $this->assertFalse($this->message->hasHeader('Content-Type')); + $this->assertTrue($this->message->hasHeader('Accept')); + + $result = $this->message->removeHeader('non-existent'); + $this->assertFalse($result); + } + + /** + * @test + */ + public function testRemoveHeaderWithValue() { + $this->message->addHeader('Accept', 'application/json'); + $this->message->addHeader('Accept', 'text/html'); + + $result = $this->message->removeHeader('Accept', 'application/json'); + $this->assertTrue($result); + + $values = $this->message->getHeader('Accept'); + $this->assertEquals(['text/html'], $values); + } + + /** + * @test + */ + public function testMultipleHeaderValues() { + $this->message->addHeader('Accept', 'application/json'); + $this->message->addHeader('Accept', 'text/html'); + + $values = $this->message->getHeader('Accept'); + $this->assertCount(2, $values); + $this->assertContains('application/json', $values); + $this->assertContains('text/html', $values); + } + + /** + * @test + */ + public function testReplaceHeaderValue() { + $this->message->addHeader('Content-Type', 'text/plain'); + $this->message->addHeader('Content-Type', 'application/json', 'text/plain'); + + $values = $this->message->getHeader('Content-Type'); + $this->assertEquals(['application/json'], $values); + } + + /** + * @test + */ + public function testCaseInsensitiveHeaders() { + $this->message->addHeader('Content-Type', 'application/json'); + + $this->assertTrue($this->message->hasHeader('content-type')); + $this->assertTrue($this->message->hasHeader('CONTENT-TYPE')); + $this->assertTrue($this->message->hasHeader('Content-Type')); + + $values = $this->message->getHeader('CONTENT-TYPE'); + $this->assertEquals(['application/json'], $values); + } + + /** + * @test + */ + public function testMultipleInstances() { + $message1 = new HttpMessage(); + $message2 = new HttpMessage(); + + $message1->addHeader('Content-Type', 'application/json'); + $message2->addHeader('Accept', 'text/html'); + + $this->assertTrue($message1->hasHeader('Content-Type')); + $this->assertFalse($message1->hasHeader('Accept')); + + $this->assertTrue($message2->hasHeader('Accept')); + $this->assertFalse($message2->hasHeader('Content-Type')); + } +} diff --git a/tests/WebFiori/Tests/Http/RequestTest.php b/tests/WebFiori/Tests/Http/RequestTest.php index 5dc11fea..91abc0c3 100644 --- a/tests/WebFiori/Tests/Http/RequestTest.php +++ b/tests/WebFiori/Tests/Http/RequestTest.php @@ -1,19 +1,22 @@ assertNull(Request::getParam('not-exist')); + $this->request = Request::createFromGlobals(); + $this->assertNull($this->request->getParam('not-exist')); } /** * @test @@ -22,7 +25,8 @@ public function testGetParam01() { putenv('REQUEST_METHOD=POST'); $_SERVER['CONTENT_TYPE'] = 'multipart/form-data'; $_POST['ok'] = 'I Ok'; - $this->assertEquals('I Ok', Request::getParam('ok')); + $this->request = Request::createFromGlobals(); + $this->assertEquals('I Ok', $this->request->getParam('ok')); } /** * @test @@ -30,7 +34,8 @@ public function testGetParam01() { public function testGetParam02() { putenv('REQUEST_METHOD=GET'); $_GET['ok'] = 'I not Ok'; - $this->assertEquals('I not Ok', Request::getParam('ok')); + $this->request = Request::createFromGlobals(); + $this->assertEquals('I not Ok', $this->request->getParam('ok')); $_GET = []; } /** @@ -38,14 +43,16 @@ public function testGetParam02() { */ public function testGetParam03() { putenv('REQUEST_METHOD=POST'); - $this->assertNull(Request::getParam('not-exist')); + $this->request = Request::createFromGlobals(); + $this->assertNull($this->request->getParam('not-exist')); } /** * @test */ public function testGetParam04() { putenv('REQUEST_METHOD=GET'); - $this->assertNull(Request::getParam('not-exist')); + $this->request = Request::createFromGlobals(); + $this->assertNull($this->request->getParam('not-exist')); } /** * @test @@ -53,7 +60,8 @@ public function testGetParam04() { public function testGetParam05() { putenv('REQUEST_METHOD=GET'); $_GET['hello'] = 'This%20is%20an%20encoded%20string.'; - $this->assertEquals('This is an encoded string.', Request::getParam('hello')); + $this->request = Request::createFromGlobals(); + $this->assertEquals('This is an encoded string.', $this->request->getParam('hello')); $_GET = []; } /** @@ -62,7 +70,8 @@ public function testGetParam05() { public function testGetParam06() { putenv('REQUEST_METHOD=GET'); $_GET['hello'] = 'This+is+an+encoded%20string.'; - $this->assertEquals('This is an encoded string.', Request::getParam('hello')); + $this->request = Request::createFromGlobals(); + $this->assertEquals('This is an encoded string.', $this->request->getParam('hello')); $_GET = []; } /** @@ -71,42 +80,49 @@ public function testGetParam06() { public function testGetParams06() { putenv('REQUEST_METHOD=GET'); $_GET['arabic'] = '%D9%86%D8%B5%20%D8%B9%D8%B1%D8%A8%D9%8A'; - $this->assertEquals('نص عربي', Request::getParam('arabic')); + $this->request = Request::createFromGlobals(); + $this->assertEquals('نص عربي', $this->request->getParam('arabic')); $_GET = []; } /** * @test */ public function testGetClientIp00() { - $this->assertEquals('127.0.0.1', Request::getClientIP()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('127.0.0.1', $this->request->getClientIP()); } /** * @test */ public function testGetClientIp01() { $_SERVER['REMOTE_ADDR'] = '::1'; - $this->assertEquals('127.0.0.1', Request::getClientIP()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('127.0.0.1', $this->request->getClientIP()); } /** * @test */ public function testGetClientIp02() { $_SERVER['REMOTE_ADDR'] = '127.5.5.6'; - $this->assertEquals('127.5.5.6', Request::getClientIP()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('127.5.5.6', $this->request->getClientIP()); } /** * @test */ public function testGetClientIp03() { $_SERVER['REMOTE_ADDR'] = '127.5A.5.6'; - $this->assertEquals('', Request::getClientIP()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('', $this->request->getClientIP()); } /** * @test */ public function testGetRequestedURL00() { + $_SERVER['HTTP_HOST'] = '127.0.0.1'; $_SERVER['PATH_INFO'] = '/my/app'; - $this->assertEquals('http://127.0.0.1/my/app', Request::getRequestedURI()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('http://127.0.0.1/my/app', $this->request->getRequestedURI()); } /** * @test @@ -115,7 +131,8 @@ public function testGetRequestedURL01() { $_SERVER['PATH_INFO'] = '/my/app'; $_GET['param1'] = 'something'; $_GET['param2'] = 'something_else'; - $this->assertEquals('http://127.0.0.1/my/app?param1=something¶m2=something_else', Request::getRequestedURI()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('http://127.0.0.1/my/app?param1=something¶m2=something_else', $this->request->getRequestedURI()); $_GET = []; } /** @@ -123,7 +140,8 @@ public function testGetRequestedURL01() { */ public function testGetRequestedURL03() { putenv('REQUEST_URI=/my/app/x'); - $this->assertEquals('http://127.0.0.1/my/app/x', Request::getRequestedURI()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('http://127.0.0.1/my/app/x', $this->request->getRequestedURI()); } /** * @test @@ -131,33 +149,49 @@ public function testGetRequestedURL03() { public function testGetRequestedURL04() { putenv('REQUEST_URI=/my/app/x?b=p'); $_GET['c'] = 'k'; - $this->assertEquals('http://127.0.0.1/my/app/x?c=k', Request::getRequestedURI()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('http://127.0.0.1/my/app/x?c=k', $this->request->getRequestedURI()); $_GET = []; } /** * @test */ public function testGetRequestedURL05() { + unset($_SERVER['PATH_INFO']); putenv('REQUEST_URI'); putenv('HTTP_REQUEST_URI=/A/B/C'); - $this->assertEquals('http://127.0.0.1/A/B/C', Request::getRequestedURI()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('http://127.0.0.1/A/B/C', $this->request->getRequestedURI()); } /** * @test */ public function testGetRequestedURL06() { + unset($_SERVER['PATH_INFO']); putenv('REQUEST_URI'); putenv('HTTP_REQUEST_URI'); $_SERVER['HTTP_X_ORIGINAL_URL'] = 'https://example.com/a/good/boy'; - $this->assertEquals('http://127.0.0.1/a/good/boy', Request::getRequestedURI()); + $this->request = Request::createFromGlobals(); + $this->assertEquals('http://127.0.0.1/a/good/boy', $this->request->getRequestedURI()); unset($_SERVER['HTTP_X_ORIGINAL_URL']); } /** * @test */ public function testGetHeaders00() { + // Store original state + $originalServer = $_SERVER; + + // Clear HTTP headers from $_SERVER + foreach ($_SERVER as $key => $value) { + if (strpos($key, 'HTTP_') === 0) { + unset($_SERVER[$key]); + } + } + $_SERVER['HTTP_CONTENT_TYPE'] = "application/json"; $_SERVER['HTTP_X_HOST'] = "Custom H"; + $this->request = Request::createFromGlobals(); $this->assertEquals([ 'content-type' => [ 'application/json' @@ -165,14 +199,19 @@ public function testGetHeaders00() { 'x-host' => [ 'Custom H' ] - ], Request::getHeadersAssoc()); + ], $this->request->getHeadersAssoc()); + + // Restore original state + $_SERVER = $originalServer; } /** * @test */ public function testGetCookie00() { - $this->assertNull(Request::getCookieValue('not-exist')); + $this->request = Request::createFromGlobals(); + $this->assertNull($this->request->getCookieValue('not-exist')); $_COOKIE['cool'] = 'cool_cookie'; - $this->assertEquals('cool_cookie', Request::getCookieValue('cool')); + $this->request = Request::createFromGlobals(); + $this->assertEquals('cool_cookie', $this->request->getCookieValue('cool')); } } diff --git a/tests/WebFiori/Tests/Http/RequestTest2.php b/tests/WebFiori/Tests/Http/RequestTest2.php new file mode 100644 index 00000000..01f9ac84 --- /dev/null +++ b/tests/WebFiori/Tests/Http/RequestTest2.php @@ -0,0 +1,250 @@ +request = new Request(); + } + + /** + * @test + */ + public function testExtendsHttpMessage() { + $this->assertInstanceOf(HttpMessage::class, $this->request); + } + + /** + * @test + */ + public function testCreateFromGlobals() { + putenv('REQUEST_METHOD=POST'); + $_SERVER['HTTP_CONTENT_TYPE'] = 'application/json'; + + $request = Request::createFromGlobals(); + + $this->assertInstanceOf(Request::class, $request); + $this->assertEquals('POST', $request->getMethod()); + } + + /** + * @test + */ + public function testGetMethod() { + $this->request->setRequestMethod('PUT'); + $this->assertEquals('PUT', $this->request->getMethod()); + } + + /** + * @test + */ + public function testGetClientIP() { + $_SERVER['REMOTE_ADDR'] = '192.168.1.100'; + $this->assertEquals('192.168.1.100', $this->request->getClientIP()); + } + + /** + * @test + */ + public function testGetClientIPDefault() { + unset($_SERVER['REMOTE_ADDR']); + $this->assertEquals('127.0.0.1', $this->request->getClientIP()); + } + + /** + * @test + */ + public function testGetContentType() { + $_SERVER['CONTENT_TYPE'] = 'application/json'; + $this->assertEquals('application/json', $this->request->getContentType()); + } + + /** + * @test + */ + public function testGetContentTypeNull() { + unset($_SERVER['CONTENT_TYPE']); + $this->assertNull($this->request->getContentType()); + } + + /** + * @test + */ + public function testGetParamFromGet() { + putenv('REQUEST_METHOD=GET'); + $_GET['test_param'] = 'test_value'; + + $request = Request::createFromGlobals(); + $this->assertEquals('test_value', $request->getParam('test_param')); + } + + /** + * @test + */ + public function testGetParamFromPost() { + putenv('REQUEST_METHOD=POST'); + $_POST['test_param'] = 'post_value'; + + $request = Request::createFromGlobals(); + $this->assertEquals('post_value', $request->getParam('test_param')); + } + + /** + * @test + */ + public function testGetParamNotExists() { + $this->assertNull($this->request->getParam('non_existent')); + } + + /** + * @test + */ + public function testGetParams() { + putenv('REQUEST_METHOD=GET'); + $_GET['param1'] = 'value1'; + $_GET['param2'] = 'value2'; + + $request = Request::createFromGlobals(); + $params = $request->getParams(); + + $this->assertArrayHasKey('param1', $params); + $this->assertArrayHasKey('param2', $params); + $this->assertEquals('value1', $params['param1']); + $this->assertEquals('value2', $params['param2']); + } + + /** + * @test + */ + public function testGetCookieValue() { + $_COOKIE['test_cookie'] = 'cookie_value'; + $this->assertEquals('cookie_value', $this->request->getCookieValue('test_cookie')); + } + + /** + * @test + */ + public function testGetCookieValueNotExists() { + $this->assertNull($this->request->getCookieValue('non_existent_cookie')); + } + + /** + * @test + */ + public function testGetPath() { + $_SERVER['REQUEST_URI'] = '/api/users?id=123'; + $request = Request::createFromGlobals(); + $this->assertEquals('/api/users', $request->getPath()); + } + + /** + * @test + */ + public function testGetPathDefault() { + unset($_SERVER['REQUEST_URI']); + unset($_SERVER['PATH_INFO']); + unset($_SERVER['SCRIPT_NAME']); + + $request = Request::createFromGlobals(); + $this->assertEquals('/', $request->getPath()); + } + + /** + * @test + */ + public function testGetRequestedURI() { + $_SERVER['REQUEST_URI'] = '/test'; + $_SERVER['HTTP_HOST'] = 'example.com'; + + $request = Request::createFromGlobals(); + $uri = $request->getRequestedURI(); + + $this->assertStringContainsString('/test', $uri); + } + + /** + * @test + */ + public function testGetRequestedURIWithAppend() { + $_SERVER['REQUEST_URI'] = '/api'; + $_SERVER['HTTP_HOST'] = 'example.com'; + + $request = Request::createFromGlobals(); + $uri = $request->getRequestedURI('users'); + + $this->assertStringContainsString('/api/users', $uri); + } + + /** + * @test + */ + public function testGetUri() { + $_SERVER['REQUEST_URI'] = '/test'; + $_SERVER['HTTP_HOST'] = 'example.com'; + + $request = Request::createFromGlobals(); + $uri = $request->getUri(); + + $this->assertInstanceOf(Uri::class, $uri); + } + + /** + * @test + */ + public function testGetAuthHeader() { + $this->request->addHeader('Authorization', 'Bearer token123'); + $authHeader = $this->request->getAuthHeader(); + + $this->assertNotNull($authHeader); + $this->assertEquals('bearer', $authHeader->getScheme()); + $this->assertEquals('token123', $authHeader->getCredentials()); + } + + /** + * @test + */ + public function testGetAuthHeaderNotExists() { + $this->assertNull($this->request->getAuthHeader()); + } + + /** + * @test + */ + public function testHeaderExtraction() { + $_SERVER['HTTP_CONTENT_TYPE'] = 'application/json'; + $_SERVER['HTTP_X_CUSTOM_HEADER'] = 'custom_value'; + + $request = Request::createFromGlobals(); + + $this->assertTrue($request->hasHeader('content-type')); + $this->assertTrue($request->hasHeader('x-custom-header')); + $this->assertEquals(['application/json'], $request->getHeader('content-type')); + $this->assertEquals(['custom_value'], $request->getHeader('x-custom-header')); + } + + /** + * @test + */ + public function testMultipleInstances() { + $request1 = new Request(); + $request2 = new Request(); + + $request1->addHeader('X-Test', 'value1'); + $request2->addHeader('X-Test', 'value2'); + + $this->assertEquals(['value1'], $request1->getHeader('x-test')); + $this->assertEquals(['value2'], $request2->getHeader('x-test')); + } +} diff --git a/tests/WebFiori/Tests/Http/RequestUriTest.php b/tests/WebFiori/Tests/Http/RequestUriTest.php new file mode 100644 index 00000000..6be29ab5 --- /dev/null +++ b/tests/WebFiori/Tests/Http/RequestUriTest.php @@ -0,0 +1,199 @@ +assertInstanceOf(Uri::class, $uri); + } + + /** + * @test + */ + public function testAddRequestMethod() { + $uri = new RequestUri('http://example.com'); + $uri->addRequestMethod('get'); + $uri->addRequestMethod('POST'); + + $methods = $uri->getRequestMethods(); + $this->assertContains('GET', $methods); + $this->assertContains('POST', $methods); + } + + /** + * @test + */ + public function testSetRequestMethods() { + $uri = new RequestUri('http://example.com'); + $uri->setRequestMethods(['get', 'post', 'put']); + + $methods = $uri->getRequestMethods(); + $this->assertEquals(['GET', 'POST', 'PUT'], $methods); + } + + /** + * @test + */ + public function testIsRequestMethodAllowed() { + $uri = new RequestUri('http://example.com'); + + // No methods set - should allow all + $this->assertTrue($uri->isRequestMethodAllowed('GET')); + $this->assertTrue($uri->isRequestMethodAllowed('post')); + + // Set specific methods + $uri->setRequestMethods(['GET', 'POST']); + $this->assertTrue($uri->isRequestMethodAllowed('get')); + $this->assertTrue($uri->isRequestMethodAllowed('POST')); + $this->assertFalse($uri->isRequestMethodAllowed('put')); + $this->assertFalse($uri->isRequestMethodAllowed('DELETE')); + } + + /** + * @test + */ + public function testIsRequestMethodAllowedNormalization() { + $uri = new RequestUri('http://example.com'); + $uri->addRequestMethod(' get '); + + $this->assertTrue($uri->isRequestMethodAllowed('GET')); + $this->assertTrue($uri->isRequestMethodAllowed('get')); + $this->assertTrue($uri->isRequestMethodAllowed(' GET ')); + } + + /** + * @test + */ + public function testHasParameters() { + $uri = new RequestUri('https://example.com/users/{id}'); + $this->assertTrue($uri->hasParameters()); + + $uri2 = new RequestUri('https://example.com/users'); + $this->assertFalse($uri2->hasParameters()); + } + + /** + * @test + */ + public function testHasParameter() { + $uri = new RequestUri('https://example.com/users/{id}/posts/{postId}'); + $this->assertTrue($uri->hasParameter('id')); + $this->assertTrue($uri->hasParameter('postId')); + $this->assertFalse($uri->hasParameter('name')); + } + + /** + * @test + */ + public function testGetParameter() { + $uri = new RequestUri('https://example.com/users/{id}'); + $param = $uri->getParameter('id'); + + $this->assertNotNull($param); + $this->assertEquals('id', $param->getName()); + + $this->assertNull($uri->getParameter('nonexistent')); + } + + /** + * @test + */ + public function testSetParameterValue() { + $uri = new RequestUri('https://example.com/users/{id}'); + $uri->setParameterValue('id', '123'); + + $param = $uri->getParameter('id'); + $this->assertEquals('123', $param->getValue()); + } + + /** + * @test + */ + public function testAddVarValue() { + $uri = new RequestUri('https://example.com/users/{id}'); + $uri->addAllowedParameterValue('id', '123'); + + $param = $uri->getParameter('id'); + $this->assertTrue($param instanceof UriParameter); + + $this->assertEquals(['123'], $param->getAllowedValues()); + $this->assertNull($param->getValue()); + + $this->assertFalse($param->setValue('132')); + $this->assertNull($param->getValue()); + + $this->assertTrue($param->setValue('123')); + $this->assertEquals('123', $param->getValue()); + } + + /** + * @test + */ + public function testEquals() { + $uri1 = new RequestUri('https://example.com/users/{id}'); + $uri1->setRequestMethods(['GET', 'POST']); + + $uri2 = new RequestUri('https://example.com/users/{id}'); + $uri2->setRequestMethods(['GET', 'POST']); + + $uri3 = new RequestUri('https://example.com/users/{name}'); + $uri3->setRequestMethods(['GET', 'POST']); + + $this->assertTrue($uri1->equals($uri2)); + $this->assertFalse($uri1->equals($uri3)); + } + + /** + * @test + */ + public function testIsAllParametersSet() { + $uri = new RequestUri('https://example.com/users/{id}/posts/{postId}'); + $this->assertFalse($uri->isAllParametersSet()); + + $uri->setParameterValue('id', '123'); + $this->assertFalse($uri->isAllParametersSet()); + + $uri->setParameterValue('postId', '456'); + $this->assertTrue($uri->isAllParametersSet()); + } + + /** + * @test + */ + public function testGetParameterValues() { + $uri = new RequestUri('https://example.com/users/{id}/posts/{postId}'); + $uri->setParameterValue('id', '123'); + $uri->setParameterValue('postId', '789'); + + $value0 = $uri->getParameterValue('id'); + $value1 = $uri->getParameterValue('postId'); + $this->assertEquals('123', $value0); + $this->assertEquals('789', $value1); + } + /** + * @test + */ + public function testSetUriPossibleVar03() { + $uri = new RequestUri('https://example.com/{first-var}/ok/{second-var}'); + $uri->addAllowedParameterValues('first-var', ['Hello','World']) + ->addAllowedParameterValues(' second-var ', ['hell','is','not','heven']) + ->addAllowedParameterValues(' secohhnd-var ', ['hell','is']); + $this->assertEquals(['Hello','World'], $uri->getAllowedParameterValues('first-var')); + $this->assertEquals(['hell','is','not','heven'], $uri->getAllowedParameterValues('second-var')); + $this->assertEquals([], $uri->getAllowedParameterValues('secohhnd-var')); + } +} diff --git a/tests/WebFiori/Tests/Http/TestServices/GetUserProfileService.php b/tests/WebFiori/Tests/Http/TestServices/GetUserProfileService.php index d0fb0709..eef1c240 100644 --- a/tests/WebFiori/Tests/Http/TestServices/GetUserProfileService.php +++ b/tests/WebFiori/Tests/Http/TestServices/GetUserProfileService.php @@ -20,7 +20,7 @@ public function __construct() { public function processRequest() { $userId = $this->getParamVal('user-id'); if ($userId === null || $userId < 0) { - $this->getManager()->sendResponse('Database Error.', self::E, 500); + $this->sendResponse('Database Error.', 500, self::E); } else { $j = new Json(); $j->add('user-name', 'Ibrahim'); diff --git a/tests/WebFiori/Tests/Http/UriTest.php b/tests/WebFiori/Tests/Http/UriTest.php index 6d159f9d..2a2ba9b6 100644 --- a/tests/WebFiori/Tests/Http/UriTest.php +++ b/tests/WebFiori/Tests/Http/UriTest.php @@ -1,8 +1,12 @@ assertEquals([], $uri->getRequestMethods()); $this->assertTrue($uri->isRequestMethodAllowed()); $uri->addRequestMethod('GET'); @@ -25,8 +29,8 @@ public function testAllowedRequestMethods00() { */ public function testAllowedRequestMethods01() { putenv('REQUEST_METHOD=GET'); - $uri = new Uri('https://example.com/'); - $uri->setRequestMethods(['POST', 'PUT', 'Get']); + $uri = new RequestUri('https://example.com/'); + $uri->setRequestMethods(['POST', 'PUT']); $this->assertEquals(['POST', 'PUT'], $uri->getRequestMethods()); $this->assertFalse($uri->isRequestMethodAllowed()); putenv('REQUEST_METHOD=PUT'); @@ -36,9 +40,9 @@ public function testAllowedRequestMethods01() { * @test */ public function testSetUriPossibleVar00() { - $uri = new Uri('https://example.com/{first-var}'); - $uri->addVarValue('first-var', 'Hello World'); - $this->assertEquals(['Hello World'], $uri->getParameterValues('first-var')); + $uri = new RequestUri('https://example.com/{first-var}'); + $uri->addAllowedParameterValue('first-var', 'Hello World'); + $this->assertEquals(['Hello World'], $uri->getAllowedParameterValues('first-var')); $this->assertEquals('/{first-var}', $uri->getPath()); $this->assertEquals(['{first-var}'], $uri->getPathArray()); } @@ -46,23 +50,23 @@ public function testSetUriPossibleVar00() { * @test */ public function testSetUriPossibleVar01() { - $uri = new Uri('https://example.com/{first-var}'); - $uri->addVarValue(' first-var ', ' Hello World '); - $this->assertEquals(['Hello World'], $uri->getParameterValues('first-var')); + $uri = new RequestUri('https://example.com/{first-var}'); + $uri->addAllowedParameterValue(' first-var ', ' Hello World '); + $this->assertEquals(['Hello World'], $uri->getAllowedParameterValues('first-var')); } /** * @test */ public function testSetUriPossibleVar02() { - $uri = new Uri('https://example.com/{first-var}'); - $uri->addVarValues('first-var', ['Hello','World']); - $this->assertEquals(['Hello','World'], $uri->getParameterValues('first-var')); + $uri = new RequestUri('https://example.com/{first-var}'); + $uri->addAllowedParameterValues('first-var', ['Hello','World']); + $this->assertEquals(['Hello','World'], $uri->getAllowedParameterValues('first-var')); } /** * @test */ public function testParams00() { - $uri = new Uri('https://example.com/{first-var}'); + $uri = new RequestUri('https://example.com/{first-var}'); $this->assertTrue($uri->hasParameter('first-var')); $this->assertFalse($uri->getParameter('first-var')->isOptional()); $this->assertNull($uri->getParameter('first-var')->getValue()); @@ -75,7 +79,7 @@ public function testParams00() { * @test */ public function testParams01() { - $uri = new Uri('https://example.com/{first-var?}'); + $uri = new RequestUri('https://example.com/{first-var?}'); $this->assertEquals('/{first-var?}', $uri->getPath()); $this->assertTrue($uri->hasParameter('first-var')); $this->assertTrue($uri->getParameter('first-var')->isOptional()); @@ -85,39 +89,29 @@ public function testParams01() { $this->assertEquals('1009', $uri->getParameter('first-var')->getValue()); $this->assertFalse($uri->setParameterValue('not-exist', 'hello')); } - /** - * @test - */ - public function testSetUriPossibleVar03() { - $uri = new Uri('https://example.com/{first-var}/ok/{second-var}'); - $uri->addVarValues('first-var', ['Hello','World']); - $uri->addVarValues(' second-var ', ['hell','is','not','heven']); - $uri->addVarValues(' secohhnd-var ', ['hell','is']); - $this->assertEquals(['Hello','World'], $uri->getParameterValues('first-var')); - $this->assertEquals(['hell','is','not','heven'], $uri->getParameterValues('second-var')); - $this->assertEquals([], $uri->getParameterValues('secohhnd-var')); - } + /** * @test */ public function testSetUriPossibleVar04() { $this->expectException('Exception'); - $this->expectExceptionMessage('Incorrect parameters order.'); - $uri = new Uri('https://example.com/{first-var}/ok/{second-var?}/{non-optional}'); + $this->expectExceptionMessage('Requred paramater cannot appear after optional'); + $uri = new RequestUri('https://example.com/{first-var}/ok/{second-var?}/{non-optional}'); } /** * @test */ public function testInvalid00() { - $this->expectException('Exception'); - $uri = new Uri(''); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('URI must be non-empty string'); + $uri = new RequestUri(''); } /** * @test */ public function testEquals00() { - $uri1 = new Uri('https://example.com/my-folder'); - $uri2 = new Uri('https://example.com/my-folder'); + $uri1 = new RequestUri('https://example.com/my-folder'); + $uri2 = new RequestUri('https://example.com/my-folder'); $this->assertTrue($uri1->equals($uri2)); $this->assertTrue($uri2->equals($uri1)); } @@ -125,8 +119,8 @@ public function testEquals00() { * @test */ public function testEquals01() { - $uri1 = new Uri('https://example.com:80/my-folder'); - $uri2 = new Uri('https://example.com/my-folder'); + $uri1 = new RequestUri('https://example.com:80/my-folder'); + $uri2 = new RequestUri('https://example.com/my-folder'); $this->assertFalse($uri1->equals($uri2)); $this->assertFalse($uri2->equals($uri1)); } @@ -134,8 +128,8 @@ public function testEquals01() { * @test */ public function testEquals02() { - $uri1 = new Uri('http://example.com/my-folder-2'); - $uri2 = new Uri('https://example.com/my-folder'); + $uri1 = new RequestUri('http://example.com/my-folder-2'); + $uri2 = new RequestUri('https://example.com/my-folder'); $this->assertFalse($uri1->equals($uri2)); $this->assertFalse($uri2->equals($uri1)); } @@ -143,17 +137,26 @@ public function testEquals02() { * @test */ public function testEquals03() { - $uri1 = new Uri('http://example.com/my-folder'); - $uri2 = new Uri('https://example.com/my-folder'); - $this->assertTrue($uri1->equals($uri2)); - $this->assertTrue($uri2->equals($uri1)); + $uri1 = new RequestUri('http://example.com/my-folder'); + $uri2 = new RequestUri('https://example.com/my-folder'); + $this->assertFalse($uri1->equals($uri2)); + $this->assertFalse($uri2->equals($uri1)); } /** * @test */ public function testEquals04() { - $uri1 = new Uri('http://example.com/my-folder/{a-var}'); - $uri2 = new Uri('https://example.com/my-folder/{a-var}'); + $uri1 = new RequestUri('http://example.com/my-folder/{a-var}'); + $uri2 = new RequestUri('https://example.com/my-folder/{a-var}'); + $this->assertFalse($uri1->equals($uri2)); + $this->assertFalse($uri2->equals($uri1)); + } + /** + * @test + */ + public function testEquals05() { + $uri1 = new RequestUri('https://example.com/my-folder/{a-var}'); + $uri2 = new RequestUri('https://example.com/my-folder/{a-var}'); $this->assertTrue($uri1->equals($uri2)); $this->assertTrue($uri2->equals($uri1)); } @@ -161,6 +164,8 @@ public function testEquals04() { * @test */ public function testGetBase00() { + $_SERVER['HTTP_HOST'] = '127.0.0.1'; + $request = Request::createFromGlobals(); $this->assertEquals('http://127.0.0.1', Uri::getBaseURL()); } /** @@ -168,7 +173,7 @@ public function testGetBase00() { */ public function testGetBase01() { $_SERVER['HTTP_HOST'] = 'webfiori.com'; - $this->assertEquals('http://webfiori.com', Uri::getBaseURL()); + $this->assertEquals('http://webfiori.com', RequestUri::getBaseURL()); } /** * @test @@ -177,7 +182,7 @@ public function testGetBase03() { $_SERVER['HTTP_HOST'] = 'webfiori.com'; $_SERVER['DOCUMENT_ROOT'] = __DIR__; define('WF_PATH_TO_APPEND', 'my-app'); - $this->assertEquals('http://webfiori.com/my-app', Uri::getBaseURL()); + $this->assertEquals('http://webfiori.com/my-app', RequestUri::getBaseURL()); } /** * @test @@ -186,7 +191,7 @@ public function testGetBase04() { $_SERVER['HTTP_HOST'] = 'webfiori.com'; $_SERVER['DOCUMENT_ROOT'] = __DIR__; $_SERVER['HTTPS'] = 'HTTPS'; - $this->assertEquals('https://webfiori.com/my-app', Uri::getBaseURL()); + $this->assertEquals('https://webfiori.com/my-app', RequestUri::getBaseURL()); } /** * @test @@ -196,13 +201,13 @@ public function testGetBase02() { $_SERVER['DOCUMENT_ROOT'] = __DIR__; $_SERVER['HTTPS'] = null; define('WF_PATH_TO_REMOVE', 'my-app'); - $this->assertEquals('http://webfiori.com/my-app', Uri::getBaseURL()); + $this->assertEquals('http://webfiori.com/my-app', RequestUri::getBaseURL()); } /** * @test */ public function testGetComponents() { - $uri = new Uri('https://example.com:8080/hell?me=ibrahim#22'); + $uri = new RequestUri('https://example.com:8080/hell?me=ibrahim#22'); $components = $uri->getComponents(); $this->assertEquals('https://example.com:8080/hell?me=ibrahim#22', $components['uri']); $this->assertEquals('https',$components['scheme']); @@ -217,8 +222,8 @@ public function testGetComponents() { * @test */ public function testEquals06() { - $uri1 = new Uri('http://example.com/my-Folder/{a-var}', false); - $uri2 = new Uri('https://example.com/my-folder/{a-var}', false); + $uri1 = new RequestUri('http://example.com/my-Folder/{a-var}', false); + $uri2 = new RequestUri('https://example.com/my-folder/{a-var}', false); $this->assertFalse($uri1->equals($uri2)); $this->assertFalse($uri2->equals($uri1)); } @@ -227,7 +232,7 @@ public function testEquals06() { */ public function testSplitURI_02() { $uri = 'https://www3.programmingacademia.com:80/{some-var}/hell/{other-var}/?do=dnt&y=#xyz'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('80',$uriObj->getPort()); } /** @@ -235,7 +240,7 @@ public function testSplitURI_02() { */ public function testSplitURI_03() { $uri = 'https://www3.programmingacademia.com:80/{some-var}/hell/{other-var}/?do=dnt&y=#xyz'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('xyz',$uriObj->getFragment()); } /** @@ -243,7 +248,7 @@ public function testSplitURI_03() { */ public function testSplitURI_04() { $uri = 'https://www3.programmingacademia.com:80/{some-var}/hell/{other-var}/?do=dnt&y=#xyz'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('do=dnt&y=',$uriObj->getQueryString()); } /** @@ -251,7 +256,7 @@ public function testSplitURI_04() { */ public function testSplitURI_05() { $uri = 'https://www3.programmingacademia.com:80/{some-var}/hell/{other-var}/?do=dnt&y=#xyz'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('https',$uriObj->getScheme()); } /** @@ -259,7 +264,7 @@ public function testSplitURI_05() { */ public function testSplitURI_06() { $uri = 'https://www3.programmingacademia.com:80/{some-var}/hell/{other-var}/?do=dnt&y=#xyz'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('/{some-var}/hell/{other-var}',$uriObj->getPath()); $this->assertTrue($uriObj->hasParameters()); $queryStrVars = $uriObj->getQueryStringVars(); @@ -277,7 +282,7 @@ public function testSplitURI_06() { */ public function testSplitURI_07() { $uri = 'https://www3.programmingacademia.com:80/{some-var}/{x}/{some-var}'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('/{some-var}/{x}/{some-var}',$uriObj->getPath()); $this->assertEquals(2,count($uriObj->getParameters())); } @@ -286,7 +291,7 @@ public function testSplitURI_07() { */ public function testSplitURI_08() { $uri = 'https://programmingacademia.com/Hello World'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('/Hello World',$uriObj->getPath()); } /** @@ -294,7 +299,7 @@ public function testSplitURI_08() { */ public function testSplitURI_09() { $uri = 'https://programmingacademia.com/Hello World? /or Not?super?'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('/Hello World? /or Not?super?',$uriObj->getPath()); } /** @@ -302,7 +307,7 @@ public function testSplitURI_09() { */ public function testSplitURI_10() { $uri = 'https://programmingacademia.com/Hello World? or Not?Yes'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('/Hello World? or Not',$uriObj->getPath()); } /** @@ -310,7 +315,7 @@ public function testSplitURI_10() { */ public function testSplitURI_11() { $uri = 'https://programmingacademia.com/Hello World#or Not#Yes'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('/Hello World#or Not',$uriObj->getPath()); $this->assertEquals('Yes',$uriObj->getFragment()); } @@ -319,7 +324,7 @@ public function testSplitURI_11() { */ public function testSplitURI_12() { $uri = 'https://programmingacademia.com/{some-var?}/ok/not/super?one=2'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('/{some-var?}/ok/not/super',$uriObj->getPath()); $this->assertEquals('one=2',$uriObj->getQueryString()); $this->assertTrue($uriObj->hasParameter('some-var')); @@ -329,18 +334,19 @@ public function testSplitURI_12() { */ public function testSplitURI_13() { $uri = 'https://programmingacademia.com/{some-var?}/{another?}/not/super'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('/{some-var?}/{another?}/not/super',$uriObj->getPath()); $this->assertTrue($uriObj->hasParameter('some-var')); $this->assertTrue($uriObj->hasParameter('another')); $this->assertTrue($uriObj->isAllParametersSet()); + } /** * @test */ public function testSplitURI_14() { $uri = 'https://programmingacademia.com/{another}/not/{some-var?}/super'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); $this->assertEquals('/{another}/not/{some-var?}/super',$uriObj->getPath()); $this->assertTrue($uriObj->hasParameter('some-var')); $this->assertTrue($uriObj->hasParameter('another')); @@ -353,7 +359,7 @@ public function testSplitURI_15() { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Empty string not allowed as variable name.'); $uri = 'https://programmingacademia.com/{}/{another}/not/super'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); } /** * @test @@ -362,6 +368,6 @@ public function testSplitURI_16() { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Empty string not allowed as variable name.'); $uri = 'https://programmingacademia.com/{?}/{another}/not/super'; - $uriObj = new Uri($uri); + $uriObj = new RequestUri($uri); } } diff --git a/tests/WebFiori/Tests/Http/WebServicesManagerTest.php b/tests/WebFiori/Tests/Http/WebServicesManagerTest.php index 84f2665c..3a819ad0 100644 --- a/tests/WebFiori/Tests/Http/WebServicesManagerTest.php +++ b/tests/WebFiori/Tests/Http/WebServicesManagerTest.php @@ -155,7 +155,7 @@ public function testConstructor00() { $this->clrearVars(); putenv('REQUEST_METHOD=GET'); $api = new SampleServicesManager(); - $this->assertEquals('GET', Request::getMethod()); + $this->assertEquals('GET', Request::createFromGlobals()->getRequestMethod()); $this->assertNull($api->getCalledServiceName()); $this->assertEquals('1.0.1',$api->getVersion()); $this->assertEquals('NO DESCRIPTION',$api->getDescription()); diff --git a/tests/phpunit10.xml b/tests/phpunit10.xml index c6ee3335..c63e0e44 100644 --- a/tests/phpunit10.xml +++ b/tests/phpunit10.xml @@ -28,6 +28,9 @@ ../WebFiori/Http/UriParameter.php ../WebFiori/Http/ObjectMapper.php ../WebFiori/Http/AuthHeader.php + ../WebFiori/Http/HttpMessage.php + ../WebFiori/Http/RequestV2.php + ../WebFiori/Http/RequestUri.php