Upgrade to 3.7.0
This commit is contained in:
28
kirby/src/Http/Cookie.php
Normal file → Executable file
28
kirby/src/Http/Cookie.php
Normal file → Executable file
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Kirby\Http;
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
/**
|
||||
@@ -41,6 +42,9 @@ class Cookie
|
||||
*/
|
||||
public static function set(string $key, string $value, array $options = []): bool
|
||||
{
|
||||
// modify CMS caching behavior
|
||||
static::trackUsage($key);
|
||||
|
||||
// extract options
|
||||
$expires = static::lifetime($options['lifetime'] ?? 0);
|
||||
$path = $options['path'] ?? '/';
|
||||
@@ -123,6 +127,10 @@ class Cookie
|
||||
if ($key === null) {
|
||||
return $_COOKIE;
|
||||
}
|
||||
|
||||
// modify CMS caching behavior
|
||||
static::trackUsage($key);
|
||||
|
||||
$value = $_COOKIE[$key] ?? null;
|
||||
return empty($value) ? $default : static::parse($value);
|
||||
}
|
||||
@@ -206,4 +214,24 @@ class Cookie
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the CMS responder that the response relies on a cookie and
|
||||
* its value (even if the cookie isn't set in the current request);
|
||||
* this ensures that the response is only cached for visitors who don't
|
||||
* have this cookie set;
|
||||
* https://github.com/getkirby/kirby/issues/4423#issuecomment-1166300526
|
||||
*
|
||||
* @param string $key
|
||||
* @return void
|
||||
*/
|
||||
protected static function trackUsage(string $key): void
|
||||
{
|
||||
// lazily request the instance for non-CMS use cases
|
||||
$kirby = App::instance(null, true);
|
||||
|
||||
if ($kirby) {
|
||||
$kirby->response()->usesCookie($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1039
kirby/src/Http/Environment.php
Executable file
1039
kirby/src/Http/Environment.php
Executable file
File diff suppressed because it is too large
Load Diff
0
kirby/src/Http/Exceptions/NextRouteException.php
Normal file → Executable file
0
kirby/src/Http/Exceptions/NextRouteException.php
Normal file → Executable file
2
kirby/src/Http/Header.php
Normal file → Executable file
2
kirby/src/Http/Header.php
Normal file → Executable file
@@ -130,7 +130,7 @@ class Header
|
||||
public static function status($code = null, bool $send = true)
|
||||
{
|
||||
$codes = static::$codes;
|
||||
$protocol = $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1';
|
||||
$protocol = Environment::getGlobally('SERVER_PROTOCOL', 'HTTP/1.1');
|
||||
|
||||
// allow full control over code and message
|
||||
if (is_string($code) === true && preg_match('/^\d{3} \w.+$/', $code) === 1) {
|
||||
|
0
kirby/src/Http/Idn.php
Normal file → Executable file
0
kirby/src/Http/Idn.php
Normal file → Executable file
0
kirby/src/Http/Params.php
Normal file → Executable file
0
kirby/src/Http/Params.php
Normal file → Executable file
0
kirby/src/Http/Path.php
Normal file → Executable file
0
kirby/src/Http/Path.php
Normal file → Executable file
0
kirby/src/Http/Query.php
Normal file → Executable file
0
kirby/src/Http/Query.php
Normal file → Executable file
0
kirby/src/Http/Remote.php
Normal file → Executable file
0
kirby/src/Http/Remote.php
Normal file → Executable file
70
kirby/src/Http/Request.php
Normal file → Executable file
70
kirby/src/Http/Request.php
Normal file → Executable file
@@ -2,8 +2,7 @@
|
||||
|
||||
namespace Kirby\Http;
|
||||
|
||||
use Kirby\Http\Request\Auth\BasicAuth;
|
||||
use Kirby\Http\Request\Auth\BearerAuth;
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Http\Request\Body;
|
||||
use Kirby\Http\Request\Files;
|
||||
use Kirby\Http\Request\Query;
|
||||
@@ -23,10 +22,16 @@ use Kirby\Toolkit\Str;
|
||||
*/
|
||||
class Request
|
||||
{
|
||||
public static $authTypes = [
|
||||
'basic' => 'Kirby\Http\Request\Auth\BasicAuth',
|
||||
'bearer' => 'Kirby\Http\Request\Auth\BearerAuth',
|
||||
'session' => 'Kirby\Http\Request\Auth\SessionAuth',
|
||||
];
|
||||
|
||||
/**
|
||||
* The auth object if available
|
||||
*
|
||||
* @var BearerAuth|BasicAuth|false|null
|
||||
* @var \Kirby\Http\Request\Auth|false|null
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
@@ -109,19 +114,19 @@ class Request
|
||||
$this->method = $this->detectRequestMethod($options['method'] ?? null);
|
||||
|
||||
if (isset($options['body']) === true) {
|
||||
$this->body = new Body($options['body']);
|
||||
$this->body = is_a($options['body'], Body::class) ? $options['body'] : new Body($options['body']);
|
||||
}
|
||||
|
||||
if (isset($options['files']) === true) {
|
||||
$this->files = new Files($options['files']);
|
||||
$this->files = is_a($options['files'], Files::class) ? $options['files'] : new Files($options['files']);
|
||||
}
|
||||
|
||||
if (isset($options['query']) === true) {
|
||||
$this->query = new Query($options['query']);
|
||||
$this->query = is_a($options['query'], Query::class) === true ? $options['query'] : new Query($options['query']);
|
||||
}
|
||||
|
||||
if (isset($options['url']) === true) {
|
||||
$this->url = new Uri($options['url']);
|
||||
$this->url = is_a($options['url'], Uri::class) === true ? $options['url'] : new Uri($options['url']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +149,7 @@ class Request
|
||||
/**
|
||||
* Returns the Auth object if authentication is set
|
||||
*
|
||||
* @return \Kirby\Http\Request\Auth\BasicAuth|\Kirby\Http\Request\Auth\BearerAuth|null
|
||||
* @return \Kirby\Http\Request\Auth|null
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
@@ -152,16 +157,31 @@ class Request
|
||||
return $this->auth;
|
||||
}
|
||||
|
||||
if ($auth = $this->options['auth'] ?? $this->header('authorization')) {
|
||||
$type = Str::before($auth, ' ');
|
||||
$token = Str::after($auth, ' ');
|
||||
$class = 'Kirby\\Http\\Request\\Auth\\' . ucfirst($type) . 'Auth';
|
||||
// lazily request the instance for non-CMS use cases
|
||||
$kirby = App::instance(null, true);
|
||||
|
||||
if (class_exists($class) === false) {
|
||||
// tell the CMS responder that the response relies on
|
||||
// the `Authorization` header and its value (even if
|
||||
// the header isn't set in the current request);
|
||||
// this ensures that the response is only cached for
|
||||
// unauthenticated visitors;
|
||||
// https://github.com/getkirby/kirby/issues/4423#issuecomment-1166300526
|
||||
if ($kirby) {
|
||||
$kirby->response()->usesAuth(true);
|
||||
}
|
||||
|
||||
if ($auth = $this->options['auth'] ?? $this->header('authorization')) {
|
||||
$type = Str::lower(Str::before($auth, ' '));
|
||||
$data = Str::after($auth, ' ');
|
||||
|
||||
$class = static::$authTypes[$type] ?? null;
|
||||
if (!$class || class_exists($class) === false) {
|
||||
return $this->auth = false;
|
||||
}
|
||||
|
||||
return $this->auth = new $class($token);
|
||||
$object = new $class($data);
|
||||
|
||||
return $this->auth = $object;
|
||||
}
|
||||
|
||||
return $this->auth = false;
|
||||
@@ -184,7 +204,7 @@ class Request
|
||||
*/
|
||||
public function cli(): bool
|
||||
{
|
||||
return Server::cli();
|
||||
return $this->options['cli'] ?? (new Environment())->cli();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,14 +240,14 @@ class Request
|
||||
$methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'];
|
||||
|
||||
// the request method can be overwritten with a header
|
||||
$methodOverride = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ?? '');
|
||||
$methodOverride = strtoupper(Environment::getGlobally('HTTP_X_HTTP_METHOD_OVERRIDE', ''));
|
||||
|
||||
if ($method === null && in_array($methodOverride, $methods) === true) {
|
||||
$method = $methodOverride;
|
||||
}
|
||||
|
||||
// final chain of options to detect the method
|
||||
$method = $method ?? $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||
$method = $method ?? Environment::getGlobally('REQUEST_METHOD', 'GET');
|
||||
|
||||
// uppercase the shit out of it
|
||||
$method = strtoupper($method);
|
||||
@@ -285,6 +305,20 @@ class Request
|
||||
return A::get($this->data(), $key, $fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the request contains
|
||||
* the `Authorization` header
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAuth(): bool
|
||||
{
|
||||
$header = $this->options['auth'] ?? $this->header('authorization');
|
||||
|
||||
return $header !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a header by key if it exists
|
||||
*
|
||||
@@ -308,7 +342,7 @@ class Request
|
||||
{
|
||||
$headers = [];
|
||||
|
||||
foreach ($_SERVER as $key => $value) {
|
||||
foreach (Environment::getGlobally() as $key => $value) {
|
||||
if (substr($key, 0, 5) !== 'HTTP_' && substr($key, 0, 14) !== 'REDIRECT_HTTP_') {
|
||||
continue;
|
||||
}
|
||||
|
61
kirby/src/Http/Request/Auth.php
Executable file
61
kirby/src/Http/Request/Auth.php
Executable file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Kirby\Http\Request;
|
||||
|
||||
/**
|
||||
* Base class for auth types
|
||||
*
|
||||
* @package Kirby Http
|
||||
* @author Lukas Bestle <lukas@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
abstract class Auth
|
||||
{
|
||||
/**
|
||||
* Raw authentication data after the first space
|
||||
* in the `Authorization` header
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $data
|
||||
*/
|
||||
public function __construct(string $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return ucfirst($this->type()) . ' ' . $this->data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw authentication data after the
|
||||
* first space in the `Authorization` header
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function data(): string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the auth type (lowercase)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function type(): string;
|
||||
}
|
17
kirby/src/Http/Request/Auth/BasicAuth.php
Normal file → Executable file
17
kirby/src/Http/Request/Auth/BasicAuth.php
Normal file → Executable file
@@ -2,12 +2,19 @@
|
||||
|
||||
namespace Kirby\Http\Request\Auth;
|
||||
|
||||
use Kirby\Http\Request\Auth;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
/**
|
||||
* Basic Authentication
|
||||
* HTTP basic authentication data
|
||||
*
|
||||
* @package Kirby Http
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
class BasicAuth extends BearerAuth
|
||||
class BasicAuth extends Auth
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
@@ -27,11 +34,11 @@ class BasicAuth extends BearerAuth
|
||||
/**
|
||||
* @param string $token
|
||||
*/
|
||||
public function __construct(string $token)
|
||||
public function __construct(string $data)
|
||||
{
|
||||
parent::__construct($token);
|
||||
parent::__construct($data);
|
||||
|
||||
$this->credentials = base64_decode($token);
|
||||
$this->credentials = base64_decode($data);
|
||||
$this->username = Str::before($this->credentials, ':');
|
||||
$this->password = Str::after($this->credentials, ':');
|
||||
}
|
||||
|
39
kirby/src/Http/Request/Auth/BearerAuth.php
Normal file → Executable file
39
kirby/src/Http/Request/Auth/BearerAuth.php
Normal file → Executable file
@@ -2,36 +2,19 @@
|
||||
|
||||
namespace Kirby\Http\Request\Auth;
|
||||
|
||||
use Kirby\Http\Request\Auth;
|
||||
|
||||
/**
|
||||
* Bearer Auth
|
||||
* Bearer token authentication data
|
||||
*
|
||||
* @package Kirby Http
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
class BearerAuth
|
||||
class BearerAuth extends Auth
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* Creates a new Bearer Auth object
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
public function __construct(string $token)
|
||||
{
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return ucfirst($this->type()) . ' ' . $this->token();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication token
|
||||
*
|
||||
@@ -39,7 +22,7 @@ class BearerAuth
|
||||
*/
|
||||
public function token(): string
|
||||
{
|
||||
return $this->token;
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
48
kirby/src/Http/Request/Auth/SessionAuth.php
Executable file
48
kirby/src/Http/Request/Auth/SessionAuth.php
Executable file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Kirby\Http\Request\Auth;
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Http\Request\Auth;
|
||||
|
||||
/**
|
||||
* Authentication data using Kirby's session
|
||||
*
|
||||
* @package Kirby Http
|
||||
* @author Lukas Bestle <lukas@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
class SessionAuth extends Auth
|
||||
{
|
||||
/**
|
||||
* Tries to return the session object
|
||||
*
|
||||
* @return \Kirby\Session\Session
|
||||
*/
|
||||
public function session()
|
||||
{
|
||||
return App::instance()->sessionHandler()->getManually($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session token
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function token(): string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return 'session';
|
||||
}
|
||||
}
|
0
kirby/src/Http/Request/Body.php
Normal file → Executable file
0
kirby/src/Http/Request/Body.php
Normal file → Executable file
0
kirby/src/Http/Request/Data.php
Normal file → Executable file
0
kirby/src/Http/Request/Data.php
Normal file → Executable file
0
kirby/src/Http/Request/Files.php
Normal file → Executable file
0
kirby/src/Http/Request/Files.php
Normal file → Executable file
0
kirby/src/Http/Request/Query.php
Normal file → Executable file
0
kirby/src/Http/Request/Query.php
Normal file → Executable file
17
kirby/src/Http/Response.php
Normal file → Executable file
17
kirby/src/Http/Response.php
Normal file → Executable file
@@ -200,6 +200,23 @@ class Response
|
||||
return new static($props);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Redirects to the given Urls
|
||||
* Urls can be relative or absolute.
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $code
|
||||
* @return void
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function go(string $url = '/', int $code = 302)
|
||||
{
|
||||
die(static::redirect($url, $code));
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for single headers
|
||||
*
|
||||
|
0
kirby/src/Http/Route.php
Normal file → Executable file
0
kirby/src/Http/Route.php
Normal file → Executable file
54
kirby/src/Http/Router.php
Normal file → Executable file
54
kirby/src/Http/Router.php
Normal file → Executable file
@@ -16,14 +16,25 @@ use Kirby\Toolkit\A;
|
||||
*/
|
||||
class Router
|
||||
{
|
||||
public static $beforeEach;
|
||||
public static $afterEach;
|
||||
/**
|
||||
* Hook that is called after each route
|
||||
*
|
||||
* @var \Closure
|
||||
*/
|
||||
protected $afterEach;
|
||||
|
||||
/**
|
||||
* Hook that is called before each route
|
||||
*
|
||||
* @var \Closure
|
||||
*/
|
||||
protected $beforeEach;
|
||||
|
||||
/**
|
||||
* Store for the current route,
|
||||
* if one can be found
|
||||
*
|
||||
* @var Route|null
|
||||
* @var \Kirby\Http\Route|null
|
||||
*/
|
||||
protected $route;
|
||||
|
||||
@@ -52,9 +63,13 @@ class Router
|
||||
* registers all the given routes
|
||||
*
|
||||
* @param array $routes
|
||||
* @param array<string, \Closure> $hooks Optional `beforeEach` and `afterEach` hooks
|
||||
*/
|
||||
public function __construct(array $routes = [])
|
||||
public function __construct(array $routes = [], array $hooks = [])
|
||||
{
|
||||
$this->beforeEach = $hooks['beforeEach'] ?? null;
|
||||
$this->afterEach = $hooks['afterEach'] ?? null;
|
||||
|
||||
foreach ($routes as $props) {
|
||||
if (isset($props['pattern'], $props['action']) === false) {
|
||||
throw new InvalidArgumentException('Invalid route parameters');
|
||||
@@ -72,7 +87,12 @@ class Router
|
||||
|
||||
foreach ($methods as $method) {
|
||||
foreach ($patterns as $pattern) {
|
||||
$this->routes[$method][] = new Route($pattern, $method, $props['action'], $props);
|
||||
$this->routes[$method][] = new Route(
|
||||
$pattern,
|
||||
$method,
|
||||
$props['action'],
|
||||
$props
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,8 +120,8 @@ class Router
|
||||
while ($loop === true) {
|
||||
$route = $this->find($path, $method, $ignore);
|
||||
|
||||
if (is_a(static::$beforeEach, 'Closure') === true) {
|
||||
(static::$beforeEach)($route, $path, $method);
|
||||
if (is_a($this->beforeEach, 'Closure') === true) {
|
||||
($this->beforeEach)($route, $path, $method);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -116,15 +136,31 @@ class Router
|
||||
$ignore[] = $route;
|
||||
}
|
||||
|
||||
if (is_a(static::$afterEach, 'Closure') === true) {
|
||||
if (is_a($this->afterEach, 'Closure') === true) {
|
||||
$final = $loop === false;
|
||||
$result = (static::$afterEach)($route, $path, $method, $result, $final);
|
||||
$result = ($this->afterEach)($route, $path, $method, $result, $final);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a micro-router and executes
|
||||
* the routing action immediately
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string|null $path
|
||||
* @param string $method
|
||||
* @param array $routes
|
||||
* @param \Closure|null $callback
|
||||
* @return mixed
|
||||
*/
|
||||
public static function execute(?string $path = null, string $method = 'GET', array $routes = [], ?Closure $callback = null)
|
||||
{
|
||||
return (new static($routes))->call($path, $method, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a Route object by path and method
|
||||
* The Route's arguments method is used to
|
||||
|
339
kirby/src/Http/Server.php
Normal file → Executable file
339
kirby/src/Http/Server.php
Normal file → Executable file
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Kirby\Http;
|
||||
|
||||
use Kirby\Toolkit\A;
|
||||
use Kirby\Toolkit\Facade;
|
||||
|
||||
/**
|
||||
* A set of methods that make it more convenient to get variables
|
||||
@@ -13,343 +13,26 @@ use Kirby\Toolkit\A;
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
* @deprecated 3.7.0 Use `Kirby\Http\Environment` instead
|
||||
* @todo Remove in 3.8.0
|
||||
*/
|
||||
class Server
|
||||
class Server extends Facade
|
||||
{
|
||||
public const HOST_FROM_SERVER = 1;
|
||||
public const HOST_FROM_HEADER = 2;
|
||||
public const HOST_ALLOW_EMPTY = 4;
|
||||
|
||||
/**
|
||||
* Cache for the cli status
|
||||
*
|
||||
* @var bool|null
|
||||
*/
|
||||
public static $cli;
|
||||
public static $hosts;
|
||||
|
||||
/**
|
||||
* List of trusted hosts
|
||||
*
|
||||
* @var array
|
||||
* @return \Kirby\Http\Environment
|
||||
*/
|
||||
public static $hosts = [];
|
||||
|
||||
/**
|
||||
* Returns the server's IP address
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function address(): string
|
||||
public static function instance()
|
||||
{
|
||||
return static::get('SERVER_ADDR', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the request is being served by the CLI
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function cli(): bool
|
||||
{
|
||||
if (static::$cli !== null) {
|
||||
return static::$cli;
|
||||
}
|
||||
|
||||
if (defined('STDIN') === true) {
|
||||
return static::$cli = true;
|
||||
}
|
||||
|
||||
$term = getenv('TERM');
|
||||
|
||||
if (substr(PHP_SAPI, 0, 3) === 'cgi' && $term && $term !== 'unknown') {
|
||||
return static::$cli = true;
|
||||
}
|
||||
|
||||
return static::$cli = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the _SERVER array
|
||||
*
|
||||
* <code>
|
||||
* Server::get('document_root');
|
||||
* // sample output: /var/www/kirby
|
||||
*
|
||||
* Server::get();
|
||||
* // returns the whole server array
|
||||
* </code>
|
||||
*
|
||||
* @param mixed $key The key to look for. Pass false or null to
|
||||
* return the entire server array.
|
||||
* @param mixed $default Optional default value, which should be
|
||||
* returned if no element has been found
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($key = null, $default = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $_SERVER;
|
||||
}
|
||||
|
||||
$key = strtoupper($key);
|
||||
$value = $_SERVER[$key] ?? $default;
|
||||
return static::sanitize($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the correct host
|
||||
*
|
||||
* @param bool $forwarded Deprecated. Todo: remove in 3.7.0
|
||||
* @return string
|
||||
*/
|
||||
public static function host(bool $forwarded = false): string
|
||||
{
|
||||
$hosts[] = static::get('SERVER_NAME');
|
||||
$hosts[] = static::get('SERVER_ADDR');
|
||||
|
||||
// insecure host parameters are only allowed when hosts
|
||||
// are validated against set of host patterns
|
||||
if (empty(static::$hosts) === false) {
|
||||
$hosts[] = static::get('HTTP_HOST');
|
||||
$hosts[] = static::get('HTTP_X_FORWARDED_HOST');
|
||||
}
|
||||
|
||||
// remove empty hosts
|
||||
$hosts = array_filter($hosts);
|
||||
|
||||
foreach ($hosts as $host) {
|
||||
if (static::isAllowedHost($host) === true) {
|
||||
return explode(':', $host)[0];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter and getter for the the static $hosts property
|
||||
*
|
||||
* $hosts = null -> return all defined hosts
|
||||
* $hosts = Server::HOST_FROM_SERVER -> []
|
||||
* $hosts = Server::HOST_FROM_HEADER -> ['*']
|
||||
* $hosts = array -> [array of trusted hosts]
|
||||
* $hosts = string -> [single trusted host]
|
||||
*
|
||||
* @param string|array|int|null $hosts
|
||||
* @return array
|
||||
*/
|
||||
public static function hosts($hosts = null): array
|
||||
{
|
||||
if ($hosts === null) {
|
||||
return static::$hosts;
|
||||
}
|
||||
|
||||
if (is_int($hosts) && $hosts & static::HOST_FROM_SERVER) {
|
||||
return static::$hosts = [];
|
||||
}
|
||||
|
||||
if (is_int($hosts) && $hosts & static::HOST_FROM_HEADER) {
|
||||
return static::$hosts = ['*'];
|
||||
}
|
||||
|
||||
// make sure hosts are always an array
|
||||
$hosts = A::wrap($hosts);
|
||||
|
||||
// return unique hosts
|
||||
return static::$hosts = array_unique($hosts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for a https request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function https(): bool
|
||||
{
|
||||
$https = $_SERVER['HTTPS'] ?? null;
|
||||
$off = ['off', null, '', 0, '0', false, 'false', -1, '-1'];
|
||||
|
||||
// check for various options to send a negative HTTPS header
|
||||
if (in_array($https, $off, true) === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check for the port
|
||||
if (static::port() === 443) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for allowed host names
|
||||
*
|
||||
* @param string $host
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAllowedHost(string $host): bool
|
||||
{
|
||||
if (empty(static::$hosts) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (static::$hosts as $pattern) {
|
||||
if (empty($pattern) === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fnmatch($pattern, $host) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server is behind a
|
||||
* proxy server.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isBehindProxy(): bool
|
||||
{
|
||||
return empty($_SERVER['HTTP_X_FORWARDED_HOST']) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the correct port number
|
||||
*
|
||||
* @param bool $forwarded Deprecated. Todo: remove in 3.7.0
|
||||
* @return int
|
||||
*/
|
||||
public static function port(bool $forwarded = false): int
|
||||
{
|
||||
$port = null;
|
||||
|
||||
// handle reverse proxy setups
|
||||
if (static::isBehindProxy() === true) {
|
||||
// based on forwarded port
|
||||
$port = static::get('HTTP_X_FORWARDED_PORT');
|
||||
|
||||
// based on the forwarded host
|
||||
if (empty($port) === true) {
|
||||
$port = (int)parse_url(static::get('HTTP_X_FORWARDED_HOST'), PHP_URL_PORT);
|
||||
}
|
||||
|
||||
// based on the forwarded proto
|
||||
if (empty($port) === true) {
|
||||
if (in_array($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? null, ['https', 'https, http']) === true) {
|
||||
$port = 443;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// based on the host
|
||||
if (empty($port) === true) {
|
||||
$port = (int)parse_url(static::get('HTTP_HOST'), PHP_URL_PORT);
|
||||
}
|
||||
|
||||
// based on server port
|
||||
if (empty($port) === true) {
|
||||
$port = static::get('SERVER_PORT');
|
||||
}
|
||||
|
||||
return $port ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with path and query
|
||||
* from the REQUEST_URI
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function requestUri(): array
|
||||
{
|
||||
$uri = static::get('REQUEST_URI', '');
|
||||
|
||||
if (Url::isAbsolute($uri) === true) {
|
||||
$uri = parse_url($uri);
|
||||
} else {
|
||||
// the fake domain is needed to make sure the URL parsing is
|
||||
// always correct. Even if there's a colon in the path for params
|
||||
$uri = parse_url('http://getkirby.com' . $uri);
|
||||
}
|
||||
|
||||
return [
|
||||
'path' => $uri['path'] ?? null,
|
||||
'query' => $uri['query'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Help to sanitize some _SERVER keys
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public static function sanitize(string $key, $value)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'SERVER_ADDR':
|
||||
case 'SERVER_NAME':
|
||||
case 'HTTP_HOST':
|
||||
case 'HTTP_X_FORWARDED_HOST':
|
||||
$value ??= '';
|
||||
$value = strtolower($value);
|
||||
$value = strip_tags($value);
|
||||
$value = basename($value);
|
||||
$value = preg_replace('![^\w.:-]+!iu', '', $value);
|
||||
$value = htmlspecialchars($value, ENT_COMPAT);
|
||||
$value = trim($value, '-');
|
||||
$value = trim($value, '.');
|
||||
break;
|
||||
case 'SERVER_PORT':
|
||||
case 'HTTP_X_FORWARDED_PORT':
|
||||
$value ??= '';
|
||||
$value = (int)(preg_replace('![^0-9]+!', '', $value));
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the php script
|
||||
* within the document root without the
|
||||
* filename of the script.
|
||||
*
|
||||
* i.e. /subfolder/index.php -> subfolder
|
||||
*
|
||||
* This can be used to build the base url
|
||||
* for subfolder installations
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function scriptPath(): string
|
||||
{
|
||||
if (static::cli() === true) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$path = $_SERVER['SCRIPT_NAME'] ?? '';
|
||||
// replace Windows backslashes
|
||||
$path = str_replace('\\', '/', $path);
|
||||
// remove the script
|
||||
$path = dirname($path);
|
||||
// replace those fucking backslashes again
|
||||
$path = str_replace('\\', '/', $path);
|
||||
// remove the leading and trailing slashes
|
||||
$path = trim($path, '/');
|
||||
|
||||
// top-level scripts don't have a path
|
||||
// and dirname() will return '.'
|
||||
if ($path === '.') {
|
||||
$path = '';
|
||||
}
|
||||
|
||||
return $path;
|
||||
return new Environment([
|
||||
'cli' => static::$cli,
|
||||
'allowed' => static::$hosts
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
45
kirby/src/Http/Uri.php
Normal file → Executable file
45
kirby/src/Http/Uri.php
Normal file → Executable file
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Kirby\Http;
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Toolkit\Properties;
|
||||
use Throwable;
|
||||
@@ -234,25 +235,21 @@ class Uri
|
||||
|
||||
/**
|
||||
* @param array $props
|
||||
* @param bool $forwarded Deprecated! Todo: remove in 3.7.0
|
||||
* @return static
|
||||
*/
|
||||
public static function current(array $props = [], bool $forwarded = false)
|
||||
public static function current(array $props = [])
|
||||
{
|
||||
if (static::$current !== null) {
|
||||
return static::$current;
|
||||
}
|
||||
|
||||
$uri = Server::requestUri();
|
||||
$url = new static(array_merge([
|
||||
'scheme' => Server::https() === true ? 'https' : 'http',
|
||||
'host' => Server::host(),
|
||||
'port' => Server::port(),
|
||||
'path' => $uri['path'],
|
||||
'query' => $uri['query'],
|
||||
], $props));
|
||||
if ($app = App::instance(null, true)) {
|
||||
$url = $app->url('current');
|
||||
} else {
|
||||
$url = (new Environment())->requestUrl();
|
||||
}
|
||||
|
||||
return $url;
|
||||
return new static($url, $props);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,6 +303,14 @@ class Uri
|
||||
return $this->query()->isNotEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function https(): bool
|
||||
{
|
||||
return $this->scheme() === 'https';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to convert the internationalized host
|
||||
* name to the human-readable UTF8 representation
|
||||
@@ -325,18 +330,18 @@ class Uri
|
||||
* or any other executed script.
|
||||
*
|
||||
* @param array $props
|
||||
* @param bool $forwarded Deprecated! Todo: remove in 3.7.0
|
||||
* @return string
|
||||
* @return static
|
||||
*/
|
||||
public static function index(array $props = [], bool $forwarded = false)
|
||||
public static function index(array $props = [])
|
||||
{
|
||||
return static::current(array_merge($props, [
|
||||
'path' => Server::scriptPath(),
|
||||
'query' => null,
|
||||
'fragment' => null,
|
||||
]));
|
||||
}
|
||||
if ($app = App::instance(null, true)) {
|
||||
$url = $app->url('index');
|
||||
} else {
|
||||
$url = (new Environment())->baseUrl();
|
||||
}
|
||||
|
||||
return new static($url, $props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the host exists
|
||||
|
5
kirby/src/Http/Url.php
Normal file → Executable file
5
kirby/src/Http/Url.php
Normal file → Executable file
@@ -100,10 +100,9 @@ class Url
|
||||
* Returns the url to the executed script
|
||||
*
|
||||
* @param array $props
|
||||
* @param bool $forwarded Deprecated! Todo: remove in 3.7.0
|
||||
* @return string
|
||||
*/
|
||||
public static function index(array $props = [], bool $forwarded = false): string
|
||||
public static function index(array $props = []): string
|
||||
{
|
||||
return Uri::index($props)->toString();
|
||||
}
|
||||
@@ -186,7 +185,7 @@ class Url
|
||||
*/
|
||||
public static function last(): string
|
||||
{
|
||||
return $_SERVER['HTTP_REFERER'] ?? '';
|
||||
return Environment::getGlobally('HTTP_REFERER', '');
|
||||
}
|
||||
|
||||
/**
|
||||
|
8
kirby/src/Http/Visitor.php
Normal file → Executable file
8
kirby/src/Http/Visitor.php
Normal file → Executable file
@@ -55,10 +55,10 @@ class Visitor
|
||||
*/
|
||||
public function __construct(array $arguments = [])
|
||||
{
|
||||
$this->ip($arguments['ip'] ?? $_SERVER['REMOTE_ADDR'] ?? '');
|
||||
$this->userAgent($arguments['userAgent'] ?? $_SERVER['HTTP_USER_AGENT'] ?? '');
|
||||
$this->acceptedLanguage($arguments['acceptedLanguage'] ?? $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '');
|
||||
$this->acceptedMimeType($arguments['acceptedMimeType'] ?? $_SERVER['HTTP_ACCEPT'] ?? '');
|
||||
$this->ip($arguments['ip'] ?? Environment::getGlobally('REMOTE_ADDR', ''));
|
||||
$this->userAgent($arguments['userAgent'] ?? Environment::getGlobally('HTTP_USER_AGENT', ''));
|
||||
$this->acceptedLanguage($arguments['acceptedLanguage'] ?? Environment::getGlobally('HTTP_ACCEPT_LANGUAGE', ''));
|
||||
$this->acceptedMimeType($arguments['acceptedMimeType'] ?? Environment::getGlobally('HTTP_ACCEPT', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user