Update Kirby and dependencies
This commit is contained in:
parent
503b339974
commit
399fa20902
439 changed files with 66915 additions and 64442 deletions
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Kirby\Http;
|
||||
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Toolkit\Str;
|
||||
|
||||
/**
|
||||
|
@ -16,194 +17,221 @@ use Kirby\Toolkit\Str;
|
|||
*/
|
||||
class Cookie
|
||||
{
|
||||
/**
|
||||
* Key to use for cookie signing
|
||||
* @var string
|
||||
*/
|
||||
public static $key = 'KirbyHttpCookieKey';
|
||||
/**
|
||||
* Key to use for cookie signing
|
||||
* @var string
|
||||
*/
|
||||
public static $key = 'KirbyHttpCookieKey';
|
||||
|
||||
/**
|
||||
* Set a new cookie
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* cookie::set('mycookie', 'hello', ['lifetime' => 60]);
|
||||
* // expires in 1 hour
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param string $key The name of the cookie
|
||||
* @param string $value The cookie content
|
||||
* @param array $options Array of options:
|
||||
* lifetime, path, domain, secure, httpOnly, sameSite
|
||||
* @return bool true: cookie was created,
|
||||
* false: cookie creation failed
|
||||
*/
|
||||
public static function set(string $key, string $value, array $options = []): bool
|
||||
{
|
||||
// extract options
|
||||
$expires = static::lifetime($options['lifetime'] ?? 0);
|
||||
$path = $options['path'] ?? '/';
|
||||
$domain = $options['domain'] ?? null;
|
||||
$secure = $options['secure'] ?? false;
|
||||
$httponly = $options['httpOnly'] ?? true;
|
||||
$samesite = $options['sameSite'] ?? 'Lax';
|
||||
/**
|
||||
* Set a new cookie
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* cookie::set('mycookie', 'hello', ['lifetime' => 60]);
|
||||
* // expires in 1 hour
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param string $key The name of the cookie
|
||||
* @param string $value The cookie content
|
||||
* @param array $options Array of options:
|
||||
* lifetime, path, domain, secure, httpOnly, sameSite
|
||||
* @return bool true: cookie was created,
|
||||
* false: cookie creation failed
|
||||
*/
|
||||
public static function set(string $key, string $value, array $options = []): bool
|
||||
{
|
||||
// modify CMS caching behavior
|
||||
static::trackUsage($key);
|
||||
|
||||
// add an HMAC signature of the value
|
||||
$value = static::hmac($value) . '+' . $value;
|
||||
// extract options
|
||||
$expires = static::lifetime($options['lifetime'] ?? 0);
|
||||
$path = $options['path'] ?? '/';
|
||||
$domain = $options['domain'] ?? null;
|
||||
$secure = $options['secure'] ?? false;
|
||||
$httponly = $options['httpOnly'] ?? true;
|
||||
$samesite = $options['sameSite'] ?? 'Lax';
|
||||
|
||||
// store that thing in the cookie global
|
||||
$_COOKIE[$key] = $value;
|
||||
// add an HMAC signature of the value
|
||||
$value = static::hmac($value) . '+' . $value;
|
||||
|
||||
// store the cookie
|
||||
$options = compact('expires', 'path', 'domain', 'secure', 'httponly', 'samesite');
|
||||
return setcookie($key, $value, $options);
|
||||
}
|
||||
// store that thing in the cookie global
|
||||
$_COOKIE[$key] = $value;
|
||||
|
||||
/**
|
||||
* Calculates the lifetime for a cookie
|
||||
*
|
||||
* @param int $minutes Number of minutes or timestamp
|
||||
* @return int
|
||||
*/
|
||||
public static function lifetime(int $minutes): int
|
||||
{
|
||||
if ($minutes > 1000000000) {
|
||||
// absolute timestamp
|
||||
return $minutes;
|
||||
} elseif ($minutes > 0) {
|
||||
// minutes from now
|
||||
return time() + ($minutes * 60);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// store the cookie
|
||||
$options = compact('expires', 'path', 'domain', 'secure', 'httponly', 'samesite');
|
||||
return setcookie($key, $value, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a cookie forever
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* cookie::forever('mycookie', 'hello');
|
||||
* // never expires
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param string $key The name of the cookie
|
||||
* @param string $value The cookie content
|
||||
* @param array $options Array of options:
|
||||
* path, domain, secure, httpOnly
|
||||
* @return bool true: cookie was created,
|
||||
* false: cookie creation failed
|
||||
*/
|
||||
public static function forever(string $key, string $value, array $options = []): bool
|
||||
{
|
||||
// 9999-12-31 if supported (lower on 32-bit servers)
|
||||
$options['lifetime'] = min(253402214400, PHP_INT_MAX);
|
||||
return static::set($key, $value, $options);
|
||||
}
|
||||
/**
|
||||
* Calculates the lifetime for a cookie
|
||||
*
|
||||
* @param int $minutes Number of minutes or timestamp
|
||||
* @return int
|
||||
*/
|
||||
public static function lifetime(int $minutes): int
|
||||
{
|
||||
if ($minutes > 1000000000) {
|
||||
// absolute timestamp
|
||||
return $minutes;
|
||||
} elseif ($minutes > 0) {
|
||||
// minutes from now
|
||||
return time() + ($minutes * 60);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cookie value
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* cookie::get('mycookie', 'peter');
|
||||
* // sample output: 'hello' or if the cookie is not set 'peter'
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param string|null $key The name of the cookie
|
||||
* @param string|null $default The default value, which should be returned
|
||||
* if the cookie has not been found
|
||||
* @return mixed The found value
|
||||
*/
|
||||
public static function get(string $key = null, string $default = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $_COOKIE;
|
||||
}
|
||||
$value = $_COOKIE[$key] ?? null;
|
||||
return empty($value) ? $default : static::parse($value);
|
||||
}
|
||||
/**
|
||||
* Stores a cookie forever
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* cookie::forever('mycookie', 'hello');
|
||||
* // never expires
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param string $key The name of the cookie
|
||||
* @param string $value The cookie content
|
||||
* @param array $options Array of options:
|
||||
* path, domain, secure, httpOnly
|
||||
* @return bool true: cookie was created,
|
||||
* false: cookie creation failed
|
||||
*/
|
||||
public static function forever(string $key, string $value, array $options = []): bool
|
||||
{
|
||||
// 9999-12-31 if supported (lower on 32-bit servers)
|
||||
$options['lifetime'] = min(253402214400, PHP_INT_MAX);
|
||||
return static::set($key, $value, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a cookie exists
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public static function exists(string $key): bool
|
||||
{
|
||||
return static::get($key) !== null;
|
||||
}
|
||||
/**
|
||||
* Get a cookie value
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* cookie::get('mycookie', 'peter');
|
||||
* // sample output: 'hello' or if the cookie is not set 'peter'
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param string|null $key The name of the cookie
|
||||
* @param string|null $default The default value, which should be returned
|
||||
* if the cookie has not been found
|
||||
* @return mixed The found value
|
||||
*/
|
||||
public static function get(string $key = null, string $default = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $_COOKIE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HMAC for the cookie value
|
||||
* Used as a cookie signature to prevent easy tampering with cookie data
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected static function hmac(string $value): string
|
||||
{
|
||||
return hash_hmac('sha1', $value, static::$key);
|
||||
}
|
||||
// modify CMS caching behavior
|
||||
static::trackUsage($key);
|
||||
|
||||
/**
|
||||
* Parses the hashed value from a cookie
|
||||
* and tries to extract the value
|
||||
*
|
||||
* @param string $string
|
||||
* @return mixed
|
||||
*/
|
||||
protected static function parse(string $string)
|
||||
{
|
||||
// if no hash-value separator is present, we can't parse the value
|
||||
if (strpos($string, '+') === false) {
|
||||
return null;
|
||||
}
|
||||
$value = $_COOKIE[$key] ?? null;
|
||||
return empty($value) ? $default : static::parse($value);
|
||||
}
|
||||
|
||||
// extract hash and value
|
||||
$hash = Str::before($string, '+');
|
||||
$value = Str::after($string, '+');
|
||||
/**
|
||||
* Checks if a cookie exists
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public static function exists(string $key): bool
|
||||
{
|
||||
return static::get($key) !== null;
|
||||
}
|
||||
|
||||
// if the hash or the value is missing at all return null
|
||||
// $value can be an empty string, $hash can't be!
|
||||
if (!is_string($hash) || $hash === '' || !is_string($value)) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Creates a HMAC for the cookie value
|
||||
* Used as a cookie signature to prevent easy tampering with cookie data
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected static function hmac(string $value): string
|
||||
{
|
||||
return hash_hmac('sha1', $value, static::$key);
|
||||
}
|
||||
|
||||
// compare the extracted hash with the hashed value
|
||||
// don't accept value if the hash is invalid
|
||||
if (hash_equals(static::hmac($value), $hash) !== true) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Parses the hashed value from a cookie
|
||||
* and tries to extract the value
|
||||
*
|
||||
* @param string $string
|
||||
* @return mixed
|
||||
*/
|
||||
protected static function parse(string $string)
|
||||
{
|
||||
// if no hash-value separator is present, we can't parse the value
|
||||
if (strpos($string, '+') === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
// extract hash and value
|
||||
$hash = Str::before($string, '+');
|
||||
$value = Str::after($string, '+');
|
||||
|
||||
/**
|
||||
* Remove a cookie
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* cookie::remove('mycookie');
|
||||
* // mycookie is now gone
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param string $key The name of the cookie
|
||||
* @return bool true: the cookie has been removed,
|
||||
* false: the cookie could not be removed
|
||||
*/
|
||||
public static function remove(string $key): bool
|
||||
{
|
||||
if (isset($_COOKIE[$key])) {
|
||||
unset($_COOKIE[$key]);
|
||||
return setcookie($key, '', 1, '/') && setcookie($key, false);
|
||||
}
|
||||
// if the hash or the value is missing at all return null
|
||||
// $value can be an empty string, $hash can't be!
|
||||
if (!is_string($hash) || $hash === '' || !is_string($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// compare the extracted hash with the hashed value
|
||||
// don't accept value if the hash is invalid
|
||||
if (hash_equals(static::hmac($value), $hash) !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a cookie
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* cookie::remove('mycookie');
|
||||
* // mycookie is now gone
|
||||
*
|
||||
* </code>
|
||||
*
|
||||
* @param string $key The name of the cookie
|
||||
* @return bool true: the cookie has been removed,
|
||||
* false: the cookie could not be removed
|
||||
*/
|
||||
public static function remove(string $key): bool
|
||||
{
|
||||
if (isset($_COOKIE[$key])) {
|
||||
unset($_COOKIE[$key]);
|
||||
return setcookie($key, '', 1, '/') && setcookie($key, false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1128
kirby/src/Http/Environment.php
Normal file
1128
kirby/src/Http/Environment.php
Normal file
File diff suppressed because it is too large
Load diff
|
@ -16,301 +16,301 @@ use Kirby\Filesystem\F;
|
|||
*/
|
||||
class Header
|
||||
{
|
||||
// configuration
|
||||
public static $codes = [
|
||||
// configuration
|
||||
public static $codes = [
|
||||
|
||||
// successful
|
||||
'_200' => 'OK',
|
||||
'_201' => 'Created',
|
||||
'_202' => 'Accepted',
|
||||
// successful
|
||||
'_200' => 'OK',
|
||||
'_201' => 'Created',
|
||||
'_202' => 'Accepted',
|
||||
|
||||
// redirection
|
||||
'_300' => 'Multiple Choices',
|
||||
'_301' => 'Moved Permanently',
|
||||
'_302' => 'Found',
|
||||
'_303' => 'See Other',
|
||||
'_304' => 'Not Modified',
|
||||
'_307' => 'Temporary Redirect',
|
||||
'_308' => 'Permanent Redirect',
|
||||
// redirection
|
||||
'_300' => 'Multiple Choices',
|
||||
'_301' => 'Moved Permanently',
|
||||
'_302' => 'Found',
|
||||
'_303' => 'See Other',
|
||||
'_304' => 'Not Modified',
|
||||
'_307' => 'Temporary Redirect',
|
||||
'_308' => 'Permanent Redirect',
|
||||
|
||||
// client error
|
||||
'_400' => 'Bad Request',
|
||||
'_401' => 'Unauthorized',
|
||||
'_402' => 'Payment Required',
|
||||
'_403' => 'Forbidden',
|
||||
'_404' => 'Not Found',
|
||||
'_405' => 'Method Not Allowed',
|
||||
'_406' => 'Not Acceptable',
|
||||
'_410' => 'Gone',
|
||||
'_418' => 'I\'m a teapot',
|
||||
'_451' => 'Unavailable For Legal Reasons',
|
||||
// client error
|
||||
'_400' => 'Bad Request',
|
||||
'_401' => 'Unauthorized',
|
||||
'_402' => 'Payment Required',
|
||||
'_403' => 'Forbidden',
|
||||
'_404' => 'Not Found',
|
||||
'_405' => 'Method Not Allowed',
|
||||
'_406' => 'Not Acceptable',
|
||||
'_410' => 'Gone',
|
||||
'_418' => 'I\'m a teapot',
|
||||
'_451' => 'Unavailable For Legal Reasons',
|
||||
|
||||
// server error
|
||||
'_500' => 'Internal Server Error',
|
||||
'_501' => 'Not Implemented',
|
||||
'_502' => 'Bad Gateway',
|
||||
'_503' => 'Service Unavailable',
|
||||
'_504' => 'Gateway Time-out'
|
||||
];
|
||||
// server error
|
||||
'_500' => 'Internal Server Error',
|
||||
'_501' => 'Not Implemented',
|
||||
'_502' => 'Bad Gateway',
|
||||
'_503' => 'Service Unavailable',
|
||||
'_504' => 'Gateway Time-out'
|
||||
];
|
||||
|
||||
/**
|
||||
* Sends a content type header
|
||||
*
|
||||
* @param string $mime
|
||||
* @param string $charset
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function contentType(string $mime, string $charset = 'UTF-8', bool $send = true)
|
||||
{
|
||||
if ($found = F::extensionToMime($mime)) {
|
||||
$mime = $found;
|
||||
}
|
||||
/**
|
||||
* Sends a content type header
|
||||
*
|
||||
* @param string $mime
|
||||
* @param string $charset
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function contentType(string $mime, string $charset = 'UTF-8', bool $send = true)
|
||||
{
|
||||
if ($found = F::extensionToMime($mime)) {
|
||||
$mime = $found;
|
||||
}
|
||||
|
||||
$header = 'Content-type: ' . $mime;
|
||||
$header = 'Content-type: ' . $mime;
|
||||
|
||||
if (empty($charset) === false) {
|
||||
$header .= '; charset=' . $charset;
|
||||
}
|
||||
if (empty($charset) === false) {
|
||||
$header .= '; charset=' . $charset;
|
||||
}
|
||||
|
||||
if ($send === false) {
|
||||
return $header;
|
||||
}
|
||||
if ($send === false) {
|
||||
return $header;
|
||||
}
|
||||
|
||||
header($header);
|
||||
}
|
||||
header($header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates headers by key and value
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param string|null $value
|
||||
* @return string
|
||||
*/
|
||||
public static function create($key, string $value = null): string
|
||||
{
|
||||
if (is_array($key) === true) {
|
||||
$headers = [];
|
||||
/**
|
||||
* Creates headers by key and value
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param string|null $value
|
||||
* @return string
|
||||
*/
|
||||
public static function create($key, string $value = null): string
|
||||
{
|
||||
if (is_array($key) === true) {
|
||||
$headers = [];
|
||||
|
||||
foreach ($key as $k => $v) {
|
||||
$headers[] = static::create($k, $v);
|
||||
}
|
||||
foreach ($key as $k => $v) {
|
||||
$headers[] = static::create($k, $v);
|
||||
}
|
||||
|
||||
return implode("\r\n", $headers);
|
||||
}
|
||||
return implode("\r\n", $headers);
|
||||
}
|
||||
|
||||
// prevent header injection by stripping any newline characters from single headers
|
||||
return str_replace(["\r", "\n"], '', $key . ': ' . $value);
|
||||
}
|
||||
// prevent header injection by stripping any newline characters from single headers
|
||||
return str_replace(["\r", "\n"], '', $key . ': ' . $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for static::contentType()
|
||||
*
|
||||
* @param string $mime
|
||||
* @param string $charset
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function type(string $mime, string $charset = 'UTF-8', bool $send = true)
|
||||
{
|
||||
return static::contentType($mime, $charset, $send);
|
||||
}
|
||||
/**
|
||||
* Shortcut for static::contentType()
|
||||
*
|
||||
* @param string $mime
|
||||
* @param string $charset
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function type(string $mime, string $charset = 'UTF-8', bool $send = true)
|
||||
{
|
||||
return static::contentType($mime, $charset, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a status header
|
||||
*
|
||||
* Checks $code against a list of known status codes. To bypass this check
|
||||
* and send a custom status code and message, use a $code string formatted
|
||||
* as 3 digits followed by a space and a message, e.g. '999 Custom Status'.
|
||||
*
|
||||
* @param int|string $code The HTTP status code
|
||||
* @param bool $send If set to false the header will be returned instead
|
||||
* @return string|void
|
||||
*/
|
||||
public static function status($code = null, bool $send = true)
|
||||
{
|
||||
$codes = static::$codes;
|
||||
$protocol = $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1';
|
||||
/**
|
||||
* Sends a status header
|
||||
*
|
||||
* Checks $code against a list of known status codes. To bypass this check
|
||||
* and send a custom status code and message, use a $code string formatted
|
||||
* as 3 digits followed by a space and a message, e.g. '999 Custom Status'.
|
||||
*
|
||||
* @param int|string $code The HTTP status code
|
||||
* @param bool $send If set to false the header will be returned instead
|
||||
* @return string|void
|
||||
*/
|
||||
public static function status($code = null, bool $send = true)
|
||||
{
|
||||
$codes = static::$codes;
|
||||
$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) {
|
||||
$message = substr(rtrim($code), 4);
|
||||
$code = substr($code, 0, 3);
|
||||
} else {
|
||||
$code = array_key_exists('_' . $code, $codes) === false ? 500 : $code;
|
||||
$message = $codes['_' . $code] ?? 'Something went wrong';
|
||||
}
|
||||
// allow full control over code and message
|
||||
if (is_string($code) === true && preg_match('/^\d{3} \w.+$/', $code) === 1) {
|
||||
$message = substr(rtrim($code), 4);
|
||||
$code = substr($code, 0, 3);
|
||||
} else {
|
||||
$code = array_key_exists('_' . $code, $codes) === false ? 500 : $code;
|
||||
$message = $codes['_' . $code] ?? 'Something went wrong';
|
||||
}
|
||||
|
||||
$header = $protocol . ' ' . $code . ' ' . $message;
|
||||
$header = $protocol . ' ' . $code . ' ' . $message;
|
||||
|
||||
if ($send === false) {
|
||||
return $header;
|
||||
}
|
||||
if ($send === false) {
|
||||
return $header;
|
||||
}
|
||||
|
||||
// try to send the header
|
||||
header($header);
|
||||
}
|
||||
// try to send the header
|
||||
header($header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 200 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function success(bool $send = true)
|
||||
{
|
||||
return static::status(200, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 200 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function success(bool $send = true)
|
||||
{
|
||||
return static::status(200, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 201 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function created(bool $send = true)
|
||||
{
|
||||
return static::status(201, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 201 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function created(bool $send = true)
|
||||
{
|
||||
return static::status(201, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 202 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function accepted(bool $send = true)
|
||||
{
|
||||
return static::status(202, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 202 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function accepted(bool $send = true)
|
||||
{
|
||||
return static::status(202, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 400 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function error(bool $send = true)
|
||||
{
|
||||
return static::status(400, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 400 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function error(bool $send = true)
|
||||
{
|
||||
return static::status(400, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 403 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function forbidden(bool $send = true)
|
||||
{
|
||||
return static::status(403, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 403 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function forbidden(bool $send = true)
|
||||
{
|
||||
return static::status(403, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 404 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function notfound(bool $send = true)
|
||||
{
|
||||
return static::status(404, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 404 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function notfound(bool $send = true)
|
||||
{
|
||||
return static::status(404, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 404 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function missing(bool $send = true)
|
||||
{
|
||||
return static::status(404, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 404 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function missing(bool $send = true)
|
||||
{
|
||||
return static::status(404, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 410 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function gone(bool $send = true)
|
||||
{
|
||||
return static::status(410, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 410 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function gone(bool $send = true)
|
||||
{
|
||||
return static::status(410, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 500 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function panic(bool $send = true)
|
||||
{
|
||||
return static::status(500, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 500 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function panic(bool $send = true)
|
||||
{
|
||||
return static::status(500, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a 503 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function unavailable(bool $send = true)
|
||||
{
|
||||
return static::status(503, $send);
|
||||
}
|
||||
/**
|
||||
* Sends a 503 header
|
||||
*
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function unavailable(bool $send = true)
|
||||
{
|
||||
return static::status(503, $send);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a redirect header
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $code
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function redirect(string $url, int $code = 302, bool $send = true)
|
||||
{
|
||||
$status = static::status($code, false);
|
||||
$location = 'Location:' . Url::unIdn($url);
|
||||
/**
|
||||
* Sends a redirect header
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $code
|
||||
* @param bool $send
|
||||
* @return string|void
|
||||
*/
|
||||
public static function redirect(string $url, int $code = 302, bool $send = true)
|
||||
{
|
||||
$status = static::status($code, false);
|
||||
$location = 'Location:' . Url::unIdn($url);
|
||||
|
||||
if ($send !== true) {
|
||||
return $status . "\r\n" . $location;
|
||||
}
|
||||
if ($send !== true) {
|
||||
return $status . "\r\n" . $location;
|
||||
}
|
||||
|
||||
header($status);
|
||||
header($location);
|
||||
exit();
|
||||
}
|
||||
header($status);
|
||||
header($location);
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends download headers for anything that is downloadable
|
||||
*
|
||||
* @param array $params Check out the defaults array for available parameters
|
||||
*/
|
||||
public static function download(array $params = [])
|
||||
{
|
||||
$defaults = [
|
||||
'name' => 'download',
|
||||
'size' => false,
|
||||
'mime' => 'application/force-download',
|
||||
'modified' => time()
|
||||
];
|
||||
/**
|
||||
* Sends download headers for anything that is downloadable
|
||||
*
|
||||
* @param array $params Check out the defaults array for available parameters
|
||||
*/
|
||||
public static function download(array $params = [])
|
||||
{
|
||||
$defaults = [
|
||||
'name' => 'download',
|
||||
'size' => false,
|
||||
'mime' => 'application/force-download',
|
||||
'modified' => time()
|
||||
];
|
||||
|
||||
$options = array_merge($defaults, $params);
|
||||
$options = array_merge($defaults, $params);
|
||||
|
||||
header('Pragma: public');
|
||||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $options['modified']) . ' GMT');
|
||||
header('Content-Disposition: attachment; filename="' . $options['name'] . '"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Pragma: public');
|
||||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $options['modified']) . ' GMT');
|
||||
header('Content-Disposition: attachment; filename="' . $options['name'] . '"');
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
|
||||
static::contentType($options['mime']);
|
||||
static::contentType($options['mime']);
|
||||
|
||||
if ($options['size']) {
|
||||
header('Content-Length: ' . $options['size']);
|
||||
}
|
||||
if ($options['size']) {
|
||||
header('Content-Length: ' . $options['size']);
|
||||
}
|
||||
|
||||
header('Connection: close');
|
||||
}
|
||||
header('Connection: close');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,61 +15,61 @@ use Kirby\Toolkit\Str;
|
|||
*/
|
||||
class Idn
|
||||
{
|
||||
/**
|
||||
* Convert domain name from IDNA ASCII to Unicode
|
||||
*
|
||||
* @param string $domain
|
||||
* @return string|false
|
||||
*/
|
||||
public static function decode(string $domain)
|
||||
{
|
||||
return idn_to_utf8($domain);
|
||||
}
|
||||
/**
|
||||
* Convert domain name from IDNA ASCII to Unicode
|
||||
*
|
||||
* @param string $domain
|
||||
* @return string|false
|
||||
*/
|
||||
public static function decode(string $domain)
|
||||
{
|
||||
return idn_to_utf8($domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert domain name to IDNA ASCII form
|
||||
*
|
||||
* @param string $domain
|
||||
* @return string|false
|
||||
*/
|
||||
public static function encode(string $domain)
|
||||
{
|
||||
return idn_to_ascii($domain);
|
||||
}
|
||||
/**
|
||||
* Convert domain name to IDNA ASCII form
|
||||
*
|
||||
* @param string $domain
|
||||
* @return string|false
|
||||
*/
|
||||
public static function encode(string $domain)
|
||||
{
|
||||
return idn_to_ascii($domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a email address to the Unicode format
|
||||
*
|
||||
* @param string $email
|
||||
* @return string
|
||||
*/
|
||||
public static function decodeEmail(string $email): string
|
||||
{
|
||||
if (Str::contains($email, 'xn--') === true) {
|
||||
$parts = Str::split($email, '@');
|
||||
$address = $parts[0];
|
||||
$domain = Idn::decode($parts[1] ?? '');
|
||||
$email = $address . '@' . $domain;
|
||||
}
|
||||
/**
|
||||
* Decodes a email address to the Unicode format
|
||||
*
|
||||
* @param string $email
|
||||
* @return string
|
||||
*/
|
||||
public static function decodeEmail(string $email): string
|
||||
{
|
||||
if (Str::contains($email, 'xn--') === true) {
|
||||
$parts = Str::split($email, '@');
|
||||
$address = $parts[0];
|
||||
$domain = Idn::decode($parts[1] ?? '');
|
||||
$email = $address . '@' . $domain;
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
return $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a email address to the Punycode format
|
||||
*
|
||||
* @param string $email
|
||||
* @return string
|
||||
*/
|
||||
public static function encodeEmail(string $email): string
|
||||
{
|
||||
if (mb_detect_encoding($email, 'ASCII', true) === false) {
|
||||
$parts = Str::split($email, '@');
|
||||
$address = $parts[0];
|
||||
$domain = Idn::encode($parts[1] ?? '');
|
||||
$email = $address . '@' . $domain;
|
||||
}
|
||||
/**
|
||||
* Encodes a email address to the Punycode format
|
||||
*
|
||||
* @param string $email
|
||||
* @return string
|
||||
*/
|
||||
public static function encodeEmail(string $email): string
|
||||
{
|
||||
if (mb_detect_encoding($email, 'ASCII', true) === false) {
|
||||
$parts = Str::split($email, '@');
|
||||
$address = $parts[0];
|
||||
$domain = Idn::encode($parts[1] ?? '');
|
||||
$email = $address . '@' . $domain;
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
return $email;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,142 +17,142 @@ use Kirby\Toolkit\Str;
|
|||
*/
|
||||
class Params extends Query
|
||||
{
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
public static $separator;
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
public static $separator;
|
||||
|
||||
/**
|
||||
* Creates a new params object
|
||||
*
|
||||
* @param array|string $params
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
if (is_string($params) === true) {
|
||||
$params = static::extract($params)['params'];
|
||||
}
|
||||
/**
|
||||
* Creates a new params object
|
||||
*
|
||||
* @param array|string $params
|
||||
*/
|
||||
public function __construct($params)
|
||||
{
|
||||
if (is_string($params) === true) {
|
||||
$params = static::extract($params)['params'];
|
||||
}
|
||||
|
||||
parent::__construct($params ?? []);
|
||||
}
|
||||
parent::__construct($params ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the params from a string or array
|
||||
*
|
||||
* @param string|array|null $path
|
||||
* @return array
|
||||
*/
|
||||
public static function extract($path = null): array
|
||||
{
|
||||
if (empty($path) === true) {
|
||||
return [
|
||||
'path' => null,
|
||||
'params' => null,
|
||||
'slash' => false
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Extract the params from a string or array
|
||||
*
|
||||
* @param string|array|null $path
|
||||
* @return array
|
||||
*/
|
||||
public static function extract($path = null): array
|
||||
{
|
||||
if (empty($path) === true) {
|
||||
return [
|
||||
'path' => null,
|
||||
'params' => null,
|
||||
'slash' => false
|
||||
];
|
||||
}
|
||||
|
||||
$slash = false;
|
||||
$slash = false;
|
||||
|
||||
if (is_string($path) === true) {
|
||||
$slash = substr($path, -1, 1) === '/';
|
||||
$path = Str::split($path, '/');
|
||||
}
|
||||
if (is_string($path) === true) {
|
||||
$slash = substr($path, -1, 1) === '/';
|
||||
$path = Str::split($path, '/');
|
||||
}
|
||||
|
||||
if (is_array($path) === true) {
|
||||
$params = [];
|
||||
$separator = static::separator();
|
||||
if (is_array($path) === true) {
|
||||
$params = [];
|
||||
$separator = static::separator();
|
||||
|
||||
foreach ($path as $index => $p) {
|
||||
if (strpos($p, $separator) === false) {
|
||||
continue;
|
||||
}
|
||||
foreach ($path as $index => $p) {
|
||||
if (strpos($p, $separator) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paramParts = Str::split($p, $separator);
|
||||
$paramKey = $paramParts[0] ?? null;
|
||||
$paramValue = $paramParts[1] ?? null;
|
||||
$paramParts = Str::split($p, $separator);
|
||||
$paramKey = $paramParts[0] ?? null;
|
||||
$paramValue = $paramParts[1] ?? null;
|
||||
|
||||
if ($paramKey !== null) {
|
||||
$params[$paramKey] = $paramValue;
|
||||
}
|
||||
if ($paramKey !== null) {
|
||||
$params[rawurldecode($paramKey)] = $paramValue ? rawurldecode($paramValue) : null;
|
||||
}
|
||||
|
||||
unset($path[$index]);
|
||||
}
|
||||
unset($path[$index]);
|
||||
}
|
||||
|
||||
return [
|
||||
'path' => $path,
|
||||
'params' => $params,
|
||||
'slash' => $slash
|
||||
];
|
||||
}
|
||||
return [
|
||||
'path' => $path,
|
||||
'params' => $params,
|
||||
'slash' => $slash
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'path' => null,
|
||||
'params' => null,
|
||||
'slash' => false
|
||||
];
|
||||
}
|
||||
return [
|
||||
'path' => null,
|
||||
'params' => null,
|
||||
'slash' => false
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the param separator according
|
||||
* to the operating system.
|
||||
*
|
||||
* Unix = ':'
|
||||
* Windows = ';'
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function separator(): string
|
||||
{
|
||||
if (static::$separator !== null) {
|
||||
return static::$separator;
|
||||
}
|
||||
/**
|
||||
* Returns the param separator according
|
||||
* to the operating system.
|
||||
*
|
||||
* Unix = ':'
|
||||
* Windows = ';'
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function separator(): string
|
||||
{
|
||||
if (static::$separator !== null) {
|
||||
return static::$separator;
|
||||
}
|
||||
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
return static::$separator = ':';
|
||||
} else {
|
||||
return static::$separator = ';';
|
||||
}
|
||||
}
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
return static::$separator = ':';
|
||||
} else {
|
||||
return static::$separator = ';';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the params object to a params string
|
||||
* which can then be used in the URL builder again
|
||||
*
|
||||
* @param bool $leadingSlash
|
||||
* @param bool $trailingSlash
|
||||
* @return string|null
|
||||
*
|
||||
* @todo The argument $leadingSlash is incompatible with
|
||||
* Query::toString($questionMark = false); the Query class
|
||||
* should be extracted into a common parent class for both
|
||||
* Query and Params
|
||||
* @psalm-suppress ParamNameMismatch
|
||||
*/
|
||||
public function toString($leadingSlash = false, $trailingSlash = false): string
|
||||
{
|
||||
if ($this->isEmpty() === true) {
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* Converts the params object to a params string
|
||||
* which can then be used in the URL builder again
|
||||
*
|
||||
* @param bool $leadingSlash
|
||||
* @param bool $trailingSlash
|
||||
* @return string|null
|
||||
*
|
||||
* @todo The argument $leadingSlash is incompatible with
|
||||
* Query::toString($questionMark = false); the Query class
|
||||
* should be extracted into a common parent class for both
|
||||
* Query and Params
|
||||
* @psalm-suppress ParamNameMismatch
|
||||
*/
|
||||
public function toString($leadingSlash = false, $trailingSlash = false): string
|
||||
{
|
||||
if ($this->isEmpty() === true) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$params = [];
|
||||
$separator = static::separator();
|
||||
$params = [];
|
||||
$separator = static::separator();
|
||||
|
||||
foreach ($this as $key => $value) {
|
||||
if ($value !== null && $value !== '') {
|
||||
$params[] = $key . $separator . $value;
|
||||
}
|
||||
}
|
||||
foreach ($this as $key => $value) {
|
||||
if ($value !== null && $value !== '') {
|
||||
$params[] = rawurlencode($key) . $separator . rawurlencode($value);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($params) === true) {
|
||||
return '';
|
||||
}
|
||||
if (empty($params) === true) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$params = implode('/', $params);
|
||||
$params = implode('/', $params);
|
||||
|
||||
$leadingSlash = $leadingSlash === true ? '/' : null;
|
||||
$trailingSlash = $trailingSlash === true ? '/' : null;
|
||||
$leadingSlash = $leadingSlash === true ? '/' : null;
|
||||
$trailingSlash = $trailingSlash === true ? '/' : null;
|
||||
|
||||
return $leadingSlash . $params . $trailingSlash;
|
||||
}
|
||||
return $leadingSlash . $params . $trailingSlash;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,31 +17,31 @@ use Kirby\Toolkit\Str;
|
|||
*/
|
||||
class Path extends Collection
|
||||
{
|
||||
public function __construct($items)
|
||||
{
|
||||
if (is_string($items) === true) {
|
||||
$items = Str::split($items, '/');
|
||||
}
|
||||
public function __construct($items)
|
||||
{
|
||||
if (is_string($items) === true) {
|
||||
$items = Str::split($items, '/');
|
||||
}
|
||||
|
||||
parent::__construct($items ?? []);
|
||||
}
|
||||
parent::__construct($items ?? []);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
public function toString(bool $leadingSlash = false, bool $trailingSlash = false): string
|
||||
{
|
||||
if (empty($this->data) === true) {
|
||||
return '';
|
||||
}
|
||||
public function toString(bool $leadingSlash = false, bool $trailingSlash = false): string
|
||||
{
|
||||
if (empty($this->data) === true) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$path = implode('/', $this->data);
|
||||
$path = implode('/', $this->data);
|
||||
|
||||
$leadingSlash = $leadingSlash === true ? '/' : null;
|
||||
$trailingSlash = $trailingSlash === true ? '/' : null;
|
||||
$leadingSlash = $leadingSlash === true ? '/' : null;
|
||||
$trailingSlash = $trailingSlash === true ? '/' : null;
|
||||
|
||||
return $leadingSlash . $path . $trailingSlash;
|
||||
}
|
||||
return $leadingSlash . $path . $trailingSlash;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,42 +17,42 @@ use Kirby\Toolkit\Obj;
|
|||
*/
|
||||
class Query extends Obj
|
||||
{
|
||||
public function __construct($query)
|
||||
{
|
||||
if (is_string($query) === true) {
|
||||
parse_str(ltrim($query, '?'), $query);
|
||||
}
|
||||
public function __construct($query)
|
||||
{
|
||||
if (is_string($query) === true) {
|
||||
parse_str(ltrim($query, '?'), $query);
|
||||
}
|
||||
|
||||
parent::__construct($query ?? []);
|
||||
}
|
||||
parent::__construct($query ?? []);
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty((array)$this) === true;
|
||||
}
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty((array)$this) === true;
|
||||
}
|
||||
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return empty((array)$this) === false;
|
||||
}
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return empty((array)$this) === false;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
public function toString($questionMark = false): string
|
||||
{
|
||||
$query = http_build_query($this, '', '&', PHP_QUERY_RFC3986);
|
||||
public function toString($questionMark = false): string
|
||||
{
|
||||
$query = http_build_query($this, '', '&', PHP_QUERY_RFC3986);
|
||||
|
||||
if (empty($query) === true) {
|
||||
return '';
|
||||
}
|
||||
if (empty($query) === true) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($questionMark === true) {
|
||||
$query = '?' . $query;
|
||||
}
|
||||
if ($questionMark === true) {
|
||||
$query = '?' . $query;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,392 +20,392 @@ use Kirby\Toolkit\Str;
|
|||
*/
|
||||
class Remote
|
||||
{
|
||||
public const CA_INTERNAL = 1;
|
||||
public const CA_SYSTEM = 2;
|
||||
public const CA_INTERNAL = 1;
|
||||
public const CA_SYSTEM = 2;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $defaults = [
|
||||
'agent' => null,
|
||||
'basicAuth' => null,
|
||||
'body' => true,
|
||||
'ca' => self::CA_INTERNAL,
|
||||
'data' => [],
|
||||
'encoding' => 'utf-8',
|
||||
'file' => null,
|
||||
'headers' => [],
|
||||
'method' => 'GET',
|
||||
'progress' => null,
|
||||
'test' => false,
|
||||
'timeout' => 10,
|
||||
];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $defaults = [
|
||||
'agent' => null,
|
||||
'basicAuth' => null,
|
||||
'body' => true,
|
||||
'ca' => self::CA_INTERNAL,
|
||||
'data' => [],
|
||||
'encoding' => 'utf-8',
|
||||
'file' => null,
|
||||
'headers' => [],
|
||||
'method' => 'GET',
|
||||
'progress' => null,
|
||||
'test' => false,
|
||||
'timeout' => 10,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $content;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $content;
|
||||
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
public $curl;
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
public $curl;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $curlopt = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $curlopt = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $errorCode;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $errorCode;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $errorMessage;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $errorMessage;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $headers = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $headers = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $info = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $info = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $options = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $options = [];
|
||||
|
||||
/**
|
||||
* Magic getter for request info data
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
$method = str_replace('-', '_', Str::kebab($method));
|
||||
return $this->info[$method] ?? null;
|
||||
}
|
||||
/**
|
||||
* Magic getter for request info data
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $method, array $arguments = [])
|
||||
{
|
||||
$method = str_replace('-', '_', Str::kebab($method));
|
||||
return $this->info[$method] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(string $url, array $options = [])
|
||||
{
|
||||
$defaults = static::$defaults;
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(string $url, array $options = [])
|
||||
{
|
||||
$defaults = static::$defaults;
|
||||
|
||||
// use the system CA store by default if
|
||||
// one has been configured in php.ini
|
||||
$cainfo = ini_get('curl.cainfo');
|
||||
if (empty($cainfo) === false && is_file($cainfo) === true) {
|
||||
$defaults['ca'] = self::CA_SYSTEM;
|
||||
}
|
||||
// use the system CA store by default if
|
||||
// one has been configured in php.ini
|
||||
$cainfo = ini_get('curl.cainfo');
|
||||
if (empty($cainfo) === false && is_file($cainfo) === true) {
|
||||
$defaults['ca'] = self::CA_SYSTEM;
|
||||
}
|
||||
|
||||
// update the defaults with App config if set;
|
||||
// request the App instance lazily
|
||||
$app = App::instance(null, true);
|
||||
if ($app !== null) {
|
||||
$defaults = array_merge($defaults, $app->option('remote', []));
|
||||
}
|
||||
// update the defaults with App config if set;
|
||||
// request the App instance lazily
|
||||
$app = App::instance(null, true);
|
||||
if ($app !== null) {
|
||||
$defaults = array_merge($defaults, $app->option('remote', []));
|
||||
}
|
||||
|
||||
// set all options
|
||||
$this->options = array_merge($defaults, $options);
|
||||
// set all options
|
||||
$this->options = array_merge($defaults, $options);
|
||||
|
||||
// add the url
|
||||
$this->options['url'] = $url;
|
||||
// add the url
|
||||
$this->options['url'] = $url;
|
||||
|
||||
// send the request
|
||||
$this->fetch();
|
||||
}
|
||||
// send the request
|
||||
$this->fetch();
|
||||
}
|
||||
|
||||
public static function __callStatic(string $method, array $arguments = [])
|
||||
{
|
||||
return new static($arguments[0], array_merge(['method' => strtoupper($method)], $arguments[1] ?? []));
|
||||
}
|
||||
public static function __callStatic(string $method, array $arguments = [])
|
||||
{
|
||||
return new static($arguments[0], array_merge(['method' => strtoupper($method)], $arguments[1] ?? []));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the http status code
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function code(): ?int
|
||||
{
|
||||
return $this->info['http_code'] ?? null;
|
||||
}
|
||||
/**
|
||||
* Returns the http status code
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function code(): ?int
|
||||
{
|
||||
return $this->info['http_code'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response content
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
/**
|
||||
* Returns the response content
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function content()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up all curl options and sends the request
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fetch()
|
||||
{
|
||||
// curl options
|
||||
$this->curlopt = [
|
||||
CURLOPT_URL => $this->options['url'],
|
||||
CURLOPT_ENCODING => $this->options['encoding'],
|
||||
CURLOPT_CONNECTTIMEOUT => $this->options['timeout'],
|
||||
CURLOPT_TIMEOUT => $this->options['timeout'],
|
||||
CURLOPT_AUTOREFERER => true,
|
||||
CURLOPT_RETURNTRANSFER => $this->options['body'],
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_HEADERFUNCTION => function ($curl, $header) {
|
||||
$parts = Str::split($header, ':');
|
||||
/**
|
||||
* Sets up all curl options and sends the request
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function fetch()
|
||||
{
|
||||
// curl options
|
||||
$this->curlopt = [
|
||||
CURLOPT_URL => $this->options['url'],
|
||||
CURLOPT_ENCODING => $this->options['encoding'],
|
||||
CURLOPT_CONNECTTIMEOUT => $this->options['timeout'],
|
||||
CURLOPT_TIMEOUT => $this->options['timeout'],
|
||||
CURLOPT_AUTOREFERER => true,
|
||||
CURLOPT_RETURNTRANSFER => $this->options['body'],
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_MAXREDIRS => 10,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_HEADERFUNCTION => function ($curl, $header) {
|
||||
$parts = Str::split($header, ':');
|
||||
|
||||
if (empty($parts[0]) === false && empty($parts[1]) === false) {
|
||||
$key = array_shift($parts);
|
||||
$this->headers[$key] = implode(':', $parts);
|
||||
}
|
||||
if (empty($parts[0]) === false && empty($parts[1]) === false) {
|
||||
$key = array_shift($parts);
|
||||
$this->headers[$key] = implode(':', $parts);
|
||||
}
|
||||
|
||||
return strlen($header);
|
||||
}
|
||||
];
|
||||
return strlen($header);
|
||||
}
|
||||
];
|
||||
|
||||
// determine the TLS CA to use
|
||||
if ($this->options['ca'] === self::CA_INTERNAL) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
$this->curlopt[CURLOPT_CAINFO] = dirname(__DIR__, 2) . '/cacert.pem';
|
||||
} elseif ($this->options['ca'] === self::CA_SYSTEM) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
} elseif ($this->options['ca'] === false) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = false;
|
||||
} elseif (
|
||||
is_string($this->options['ca']) === true &&
|
||||
is_file($this->options['ca']) === true
|
||||
) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
$this->curlopt[CURLOPT_CAINFO] = $this->options['ca'];
|
||||
} elseif (
|
||||
is_string($this->options['ca']) === true &&
|
||||
is_dir($this->options['ca']) === true
|
||||
) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
$this->curlopt[CURLOPT_CAPATH] = $this->options['ca'];
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid "ca" option for the Remote class');
|
||||
}
|
||||
// determine the TLS CA to use
|
||||
if ($this->options['ca'] === self::CA_INTERNAL) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
$this->curlopt[CURLOPT_CAINFO] = dirname(__DIR__, 2) . '/cacert.pem';
|
||||
} elseif ($this->options['ca'] === self::CA_SYSTEM) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
} elseif ($this->options['ca'] === false) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = false;
|
||||
} elseif (
|
||||
is_string($this->options['ca']) === true &&
|
||||
is_file($this->options['ca']) === true
|
||||
) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
$this->curlopt[CURLOPT_CAINFO] = $this->options['ca'];
|
||||
} elseif (
|
||||
is_string($this->options['ca']) === true &&
|
||||
is_dir($this->options['ca']) === true
|
||||
) {
|
||||
$this->curlopt[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
$this->curlopt[CURLOPT_CAPATH] = $this->options['ca'];
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid "ca" option for the Remote class');
|
||||
}
|
||||
|
||||
// add the progress
|
||||
if (is_callable($this->options['progress']) === true) {
|
||||
$this->curlopt[CURLOPT_NOPROGRESS] = false;
|
||||
$this->curlopt[CURLOPT_PROGRESSFUNCTION] = $this->options['progress'];
|
||||
}
|
||||
// add the progress
|
||||
if (is_callable($this->options['progress']) === true) {
|
||||
$this->curlopt[CURLOPT_NOPROGRESS] = false;
|
||||
$this->curlopt[CURLOPT_PROGRESSFUNCTION] = $this->options['progress'];
|
||||
}
|
||||
|
||||
// add all headers
|
||||
if (empty($this->options['headers']) === false) {
|
||||
// convert associative arrays to strings
|
||||
$headers = [];
|
||||
foreach ($this->options['headers'] as $key => $value) {
|
||||
if (is_string($key) === true) {
|
||||
$headers[] = $key . ': ' . $value;
|
||||
} else {
|
||||
$headers[] = $value;
|
||||
}
|
||||
}
|
||||
// add all headers
|
||||
if (empty($this->options['headers']) === false) {
|
||||
// convert associative arrays to strings
|
||||
$headers = [];
|
||||
foreach ($this->options['headers'] as $key => $value) {
|
||||
if (is_string($key) === true) {
|
||||
$headers[] = $key . ': ' . $value;
|
||||
} else {
|
||||
$headers[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->curlopt[CURLOPT_HTTPHEADER] = $headers;
|
||||
}
|
||||
$this->curlopt[CURLOPT_HTTPHEADER] = $headers;
|
||||
}
|
||||
|
||||
// add HTTP Basic authentication
|
||||
if (empty($this->options['basicAuth']) === false) {
|
||||
$this->curlopt[CURLOPT_USERPWD] = $this->options['basicAuth'];
|
||||
}
|
||||
// add HTTP Basic authentication
|
||||
if (empty($this->options['basicAuth']) === false) {
|
||||
$this->curlopt[CURLOPT_USERPWD] = $this->options['basicAuth'];
|
||||
}
|
||||
|
||||
// add the user agent
|
||||
if (empty($this->options['agent']) === false) {
|
||||
$this->curlopt[CURLOPT_USERAGENT] = $this->options['agent'];
|
||||
}
|
||||
// add the user agent
|
||||
if (empty($this->options['agent']) === false) {
|
||||
$this->curlopt[CURLOPT_USERAGENT] = $this->options['agent'];
|
||||
}
|
||||
|
||||
// do some request specific stuff
|
||||
switch (strtoupper($this->options['method'])) {
|
||||
case 'POST':
|
||||
$this->curlopt[CURLOPT_POST] = true;
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'POST';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
break;
|
||||
case 'PUT':
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'PUT';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
// do some request specific stuff
|
||||
switch (strtoupper($this->options['method'])) {
|
||||
case 'POST':
|
||||
$this->curlopt[CURLOPT_POST] = true;
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'POST';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
break;
|
||||
case 'PUT':
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'PUT';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
|
||||
// put a file
|
||||
if ($this->options['file']) {
|
||||
$this->curlopt[CURLOPT_INFILE] = fopen($this->options['file'], 'r');
|
||||
$this->curlopt[CURLOPT_INFILESIZE] = F::size($this->options['file']);
|
||||
}
|
||||
break;
|
||||
case 'PATCH':
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'PATCH';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
break;
|
||||
case 'DELETE':
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'DELETE';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
break;
|
||||
case 'HEAD':
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'HEAD';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
$this->curlopt[CURLOPT_NOBODY] = true;
|
||||
break;
|
||||
}
|
||||
// put a file
|
||||
if ($this->options['file']) {
|
||||
$this->curlopt[CURLOPT_INFILE] = fopen($this->options['file'], 'r');
|
||||
$this->curlopt[CURLOPT_INFILESIZE] = F::size($this->options['file']);
|
||||
}
|
||||
break;
|
||||
case 'PATCH':
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'PATCH';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
break;
|
||||
case 'DELETE':
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'DELETE';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
break;
|
||||
case 'HEAD':
|
||||
$this->curlopt[CURLOPT_CUSTOMREQUEST] = 'HEAD';
|
||||
$this->curlopt[CURLOPT_POSTFIELDS] = $this->postfields($this->options['data']);
|
||||
$this->curlopt[CURLOPT_NOBODY] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->options['test'] === true) {
|
||||
return $this;
|
||||
}
|
||||
if ($this->options['test'] === true) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// start a curl request
|
||||
$this->curl = curl_init();
|
||||
// start a curl request
|
||||
$this->curl = curl_init();
|
||||
|
||||
curl_setopt_array($this->curl, $this->curlopt);
|
||||
curl_setopt_array($this->curl, $this->curlopt);
|
||||
|
||||
$this->content = curl_exec($this->curl);
|
||||
$this->info = curl_getinfo($this->curl);
|
||||
$this->errorCode = curl_errno($this->curl);
|
||||
$this->errorMessage = curl_error($this->curl);
|
||||
$this->content = curl_exec($this->curl);
|
||||
$this->info = curl_getinfo($this->curl);
|
||||
$this->errorCode = curl_errno($this->curl);
|
||||
$this->errorMessage = curl_error($this->curl);
|
||||
|
||||
if ($this->errorCode) {
|
||||
throw new Exception($this->errorMessage, $this->errorCode);
|
||||
}
|
||||
if ($this->errorCode) {
|
||||
throw new Exception($this->errorMessage, $this->errorCode);
|
||||
}
|
||||
|
||||
curl_close($this->curl);
|
||||
curl_close($this->curl);
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to send a GET request
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $params
|
||||
* @return static
|
||||
*/
|
||||
public static function get(string $url, array $params = [])
|
||||
{
|
||||
$defaults = [
|
||||
'method' => 'GET',
|
||||
'data' => [],
|
||||
];
|
||||
/**
|
||||
* Static method to send a GET request
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $params
|
||||
* @return static
|
||||
*/
|
||||
public static function get(string $url, array $params = [])
|
||||
{
|
||||
$defaults = [
|
||||
'method' => 'GET',
|
||||
'data' => [],
|
||||
];
|
||||
|
||||
$options = array_merge($defaults, $params);
|
||||
$query = http_build_query($options['data']);
|
||||
$options = array_merge($defaults, $params);
|
||||
$query = http_build_query($options['data']);
|
||||
|
||||
if (empty($query) === false) {
|
||||
$url = Url::hasQuery($url) === true ? $url . '&' . $query : $url . '?' . $query;
|
||||
}
|
||||
if (empty($query) === false) {
|
||||
$url = Url::hasQuery($url) === true ? $url . '&' . $query : $url . '?' . $query;
|
||||
}
|
||||
|
||||
// remove the data array from the options
|
||||
unset($options['data']);
|
||||
// remove the data array from the options
|
||||
unset($options['data']);
|
||||
|
||||
return new static($url, $options);
|
||||
}
|
||||
return new static($url, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all received headers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function headers(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
/**
|
||||
* Returns all received headers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function headers(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request info
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function info(): array
|
||||
{
|
||||
return $this->info;
|
||||
}
|
||||
/**
|
||||
* Returns the request info
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function info(): array
|
||||
{
|
||||
return $this->info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the response content
|
||||
*
|
||||
* @param bool $array decode as array or object
|
||||
* @return array|\stdClass
|
||||
*/
|
||||
public function json(bool $array = true)
|
||||
{
|
||||
return json_decode($this->content(), $array);
|
||||
}
|
||||
/**
|
||||
* Decode the response content
|
||||
*
|
||||
* @param bool $array decode as array or object
|
||||
* @return array|\stdClass
|
||||
*/
|
||||
public function json(bool $array = true)
|
||||
{
|
||||
return json_decode($this->content(), $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function method(): string
|
||||
{
|
||||
return $this->options['method'];
|
||||
}
|
||||
/**
|
||||
* Returns the request method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function method(): string
|
||||
{
|
||||
return $this->options['method'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all options which have been
|
||||
* set for the current request
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function options(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
/**
|
||||
* Returns all options which have been
|
||||
* set for the current request
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function options(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to handle post field data
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return mixed
|
||||
*/
|
||||
protected function postfields($data)
|
||||
{
|
||||
if (is_object($data) || is_array($data)) {
|
||||
return http_build_query($data);
|
||||
} else {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Internal method to handle post field data
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return mixed
|
||||
*/
|
||||
protected function postfields($data)
|
||||
{
|
||||
if (is_object($data) || is_array($data)) {
|
||||
return http_build_query($data);
|
||||
} else {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to init this class and send a request
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $params
|
||||
* @return static
|
||||
*/
|
||||
public static function request(string $url, array $params = [])
|
||||
{
|
||||
return new static($url, $params);
|
||||
}
|
||||
/**
|
||||
* Static method to init this class and send a request
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $params
|
||||
* @return static
|
||||
*/
|
||||
public static function request(string $url, array $params = [])
|
||||
{
|
||||
return new static($url, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request Url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function url(): string
|
||||
{
|
||||
return $this->options['url'];
|
||||
}
|
||||
/**
|
||||
* Returns the request Url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function url(): string
|
||||
{
|
||||
return $this->options['url'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,390 +22,425 @@ use Kirby\Toolkit\Str;
|
|||
*/
|
||||
class Request
|
||||
{
|
||||
/**
|
||||
* The auth object if available
|
||||
*
|
||||
* @var BearerAuth|BasicAuth|false|null
|
||||
*/
|
||||
protected $auth;
|
||||
public static $authTypes = [
|
||||
'basic' => 'Kirby\Http\Request\Auth\BasicAuth',
|
||||
'bearer' => 'Kirby\Http\Request\Auth\BearerAuth',
|
||||
'session' => 'Kirby\Http\Request\Auth\SessionAuth',
|
||||
];
|
||||
|
||||
/**
|
||||
* The Body object is a wrapper around
|
||||
* the request body, which parses the contents
|
||||
* of the body and provides an API to fetch
|
||||
* particular parts of the body
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* `$request->body()->get('foo')`
|
||||
*
|
||||
* @var Body
|
||||
*/
|
||||
protected $body;
|
||||
/**
|
||||
* The auth object if available
|
||||
*
|
||||
* @var \Kirby\Http\Request\Auth|false|null
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* The Files object is a wrapper around
|
||||
* the $_FILES global. It sanitizes the
|
||||
* $_FILES array and provides an API to fetch
|
||||
* individual files by key
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* `$request->files()->get('upload')['size']`
|
||||
* `$request->file('upload')['size']`
|
||||
*
|
||||
* @var Files
|
||||
*/
|
||||
protected $files;
|
||||
/**
|
||||
* The Body object is a wrapper around
|
||||
* the request body, which parses the contents
|
||||
* of the body and provides an API to fetch
|
||||
* particular parts of the body
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* `$request->body()->get('foo')`
|
||||
*
|
||||
* @var Body
|
||||
*/
|
||||
protected $body;
|
||||
|
||||
/**
|
||||
* The Method type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $method;
|
||||
/**
|
||||
* The Files object is a wrapper around
|
||||
* the $_FILES global. It sanitizes the
|
||||
* $_FILES array and provides an API to fetch
|
||||
* individual files by key
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* `$request->files()->get('upload')['size']`
|
||||
* `$request->file('upload')['size']`
|
||||
*
|
||||
* @var Files
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* All options that have been passed to
|
||||
* the request in the constructor
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
/**
|
||||
* The Method type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $method;
|
||||
|
||||
/**
|
||||
* The Query object is a wrapper around
|
||||
* the URL query string, which parses the
|
||||
* string and provides a clean API to fetch
|
||||
* particular parts of the query
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* `$request->query()->get('foo')`
|
||||
*
|
||||
* @var Query
|
||||
*/
|
||||
protected $query;
|
||||
/**
|
||||
* All options that have been passed to
|
||||
* the request in the constructor
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* Request URL object
|
||||
*
|
||||
* @var Uri
|
||||
*/
|
||||
protected $url;
|
||||
/**
|
||||
* The Query object is a wrapper around
|
||||
* the URL query string, which parses the
|
||||
* string and provides a clean API to fetch
|
||||
* particular parts of the query
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* `$request->query()->get('foo')`
|
||||
*
|
||||
* @var Query
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* Creates a new Request object
|
||||
* You can either pass your own request
|
||||
* data via the $options array or use
|
||||
* the data from the incoming request.
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->method = $this->detectRequestMethod($options['method'] ?? null);
|
||||
/**
|
||||
* Request URL object
|
||||
*
|
||||
* @var Uri
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
if (isset($options['body']) === true) {
|
||||
$this->body = new Body($options['body']);
|
||||
}
|
||||
/**
|
||||
* Creates a new Request object
|
||||
* You can either pass your own request
|
||||
* data via the $options array or use
|
||||
* the data from the incoming request.
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
$this->method = $this->detectRequestMethod($options['method'] ?? null);
|
||||
|
||||
if (isset($options['files']) === true) {
|
||||
$this->files = new Files($options['files']);
|
||||
}
|
||||
if (isset($options['body']) === true) {
|
||||
$this->body = is_a($options['body'], Body::class) ? $options['body'] : new Body($options['body']);
|
||||
}
|
||||
|
||||
if (isset($options['query']) === true) {
|
||||
$this->query = new Query($options['query']);
|
||||
}
|
||||
if (isset($options['files']) === true) {
|
||||
$this->files = is_a($options['files'], Files::class) ? $options['files'] : new Files($options['files']);
|
||||
}
|
||||
|
||||
if (isset($options['url']) === true) {
|
||||
$this->url = new Uri($options['url']);
|
||||
}
|
||||
}
|
||||
if (isset($options['query']) === true) {
|
||||
$this->query = is_a($options['query'], Query::class) === true ? $options['query'] : new Query($options['query']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return [
|
||||
'body' => $this->body(),
|
||||
'files' => $this->files(),
|
||||
'method' => $this->method(),
|
||||
'query' => $this->query(),
|
||||
'url' => $this->url()->toString()
|
||||
];
|
||||
}
|
||||
if (isset($options['url']) === true) {
|
||||
$this->url = is_a($options['url'], Uri::class) === true ? $options['url'] : new Uri($options['url']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Auth object if authentication is set
|
||||
*
|
||||
* @return \Kirby\Http\Request\Auth\BasicAuth|\Kirby\Http\Request\Auth\BearerAuth|null
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
if ($this->auth !== null) {
|
||||
return $this->auth;
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return [
|
||||
'body' => $this->body(),
|
||||
'files' => $this->files(),
|
||||
'method' => $this->method(),
|
||||
'query' => $this->query(),
|
||||
'url' => $this->url()->toString()
|
||||
];
|
||||
}
|
||||
|
||||
if ($auth = $this->options['auth'] ?? $this->header('authorization')) {
|
||||
$type = Str::before($auth, ' ');
|
||||
$token = Str::after($auth, ' ');
|
||||
$class = 'Kirby\\Http\\Request\\Auth\\' . ucfirst($type) . 'Auth';
|
||||
/**
|
||||
* Returns the Auth object if authentication is set
|
||||
*
|
||||
* @return \Kirby\Http\Request\Auth|null
|
||||
*/
|
||||
public function auth()
|
||||
{
|
||||
if ($this->auth !== null) {
|
||||
return $this->auth;
|
||||
}
|
||||
|
||||
if (class_exists($class) === false) {
|
||||
return $this->auth = false;
|
||||
}
|
||||
// lazily request the instance for non-CMS use cases
|
||||
$kirby = App::instance(null, true);
|
||||
|
||||
return $this->auth = new $class($token);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
return $this->auth = false;
|
||||
}
|
||||
if ($auth = $this->options['auth'] ?? $this->header('authorization')) {
|
||||
$type = Str::lower(Str::before($auth, ' '));
|
||||
$data = Str::after($auth, ' ');
|
||||
|
||||
/**
|
||||
* Returns the Body object
|
||||
*
|
||||
* @return \Kirby\Http\Request\Body
|
||||
*/
|
||||
public function body()
|
||||
{
|
||||
return $this->body ??= new Body();
|
||||
}
|
||||
$class = static::$authTypes[$type] ?? null;
|
||||
if (!$class || class_exists($class) === false) {
|
||||
return $this->auth = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the request has been made from the command line
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function cli(): bool
|
||||
{
|
||||
return Server::cli();
|
||||
}
|
||||
$object = new $class($data);
|
||||
|
||||
/**
|
||||
* Returns a CSRF token if stored in a header or the query
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function csrf(): ?string
|
||||
{
|
||||
return $this->header('x-csrf') ?? $this->query()->get('csrf');
|
||||
}
|
||||
return $this->auth = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request input as array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return array_merge($this->body()->toArray(), $this->query()->toArray());
|
||||
}
|
||||
return $this->auth = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the request method from various
|
||||
* options: given method, query string, server vars
|
||||
*
|
||||
* @param string $method
|
||||
* @return string
|
||||
*/
|
||||
public function detectRequestMethod(string $method = null): string
|
||||
{
|
||||
// all possible methods
|
||||
$methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'];
|
||||
/**
|
||||
* Returns the Body object
|
||||
*
|
||||
* @return \Kirby\Http\Request\Body
|
||||
*/
|
||||
public function body()
|
||||
{
|
||||
return $this->body ??= new Body();
|
||||
}
|
||||
|
||||
// the request method can be overwritten with a header
|
||||
$methodOverride = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ?? '');
|
||||
/**
|
||||
* Checks if the request has been made from the command line
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function cli(): bool
|
||||
{
|
||||
return $this->options['cli'] ?? (new Environment())->cli();
|
||||
}
|
||||
|
||||
if ($method === null && in_array($methodOverride, $methods) === true) {
|
||||
$method = $methodOverride;
|
||||
}
|
||||
/**
|
||||
* Returns a CSRF token if stored in a header or the query
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function csrf(): ?string
|
||||
{
|
||||
return $this->header('x-csrf') ?? $this->query()->get('csrf');
|
||||
}
|
||||
|
||||
// final chain of options to detect the method
|
||||
$method = $method ?? $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||
/**
|
||||
* Returns the request input as array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return array_merge($this->body()->toArray(), $this->query()->toArray());
|
||||
}
|
||||
|
||||
// uppercase the shit out of it
|
||||
$method = strtoupper($method);
|
||||
/**
|
||||
* Detect the request method from various
|
||||
* options: given method, query string, server vars
|
||||
*
|
||||
* @param string $method
|
||||
* @return string
|
||||
*/
|
||||
public function detectRequestMethod(string $method = null): string
|
||||
{
|
||||
// all possible methods
|
||||
$methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'];
|
||||
|
||||
// sanitize the method
|
||||
if (in_array($method, $methods) === false) {
|
||||
$method = 'GET';
|
||||
}
|
||||
// the request method can be overwritten with a header
|
||||
$methodOverride = strtoupper(Environment::getGlobally('HTTP_X_HTTP_METHOD_OVERRIDE', ''));
|
||||
|
||||
return $method;
|
||||
}
|
||||
if ($method === null && in_array($methodOverride, $methods) === true) {
|
||||
$method = $methodOverride;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the domain
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function domain(): string
|
||||
{
|
||||
return $this->url()->domain();
|
||||
}
|
||||
// final chain of options to detect the method
|
||||
$method = $method ?? Environment::getGlobally('REQUEST_METHOD', 'GET');
|
||||
|
||||
/**
|
||||
* Fetches a single file array
|
||||
* from the Files object by key
|
||||
*
|
||||
* @param string $key
|
||||
* @return array|null
|
||||
*/
|
||||
public function file(string $key)
|
||||
{
|
||||
return $this->files()->get($key);
|
||||
}
|
||||
// uppercase the shit out of it
|
||||
$method = strtoupper($method);
|
||||
|
||||
/**
|
||||
* Returns the Files object
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
return $this->files ??= new Files();
|
||||
}
|
||||
// sanitize the method
|
||||
if (in_array($method, $methods) === false) {
|
||||
$method = 'GET';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any data field from the request
|
||||
* if it exists
|
||||
*
|
||||
* @param string|null|array $key
|
||||
* @param mixed $fallback
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key = null, $fallback = null)
|
||||
{
|
||||
return A::get($this->data(), $key, $fallback);
|
||||
}
|
||||
return $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a header by key if it exists
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $fallback
|
||||
* @return mixed
|
||||
*/
|
||||
public function header(string $key, $fallback = null)
|
||||
{
|
||||
$headers = array_change_key_case($this->headers());
|
||||
return $headers[strtolower($key)] ?? $fallback;
|
||||
}
|
||||
/**
|
||||
* Returns the domain
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function domain(): string
|
||||
{
|
||||
return $this->url()->domain();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all headers with polyfill for
|
||||
* missing getallheaders function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function headers(): array
|
||||
{
|
||||
$headers = [];
|
||||
/**
|
||||
* Fetches a single file array
|
||||
* from the Files object by key
|
||||
*
|
||||
* @param string $key
|
||||
* @return array|null
|
||||
*/
|
||||
public function file(string $key)
|
||||
{
|
||||
return $this->files()->get($key);
|
||||
}
|
||||
|
||||
foreach ($_SERVER as $key => $value) {
|
||||
if (substr($key, 0, 5) !== 'HTTP_' && substr($key, 0, 14) !== 'REDIRECT_HTTP_') {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Returns the Files object
|
||||
*
|
||||
* @return \Kirby\Cms\Files
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
return $this->files ??= new Files();
|
||||
}
|
||||
|
||||
// remove HTTP_
|
||||
$key = str_replace(['REDIRECT_HTTP_', 'HTTP_'], '', $key);
|
||||
/**
|
||||
* Returns any data field from the request
|
||||
* if it exists
|
||||
*
|
||||
* @param string|null|array $key
|
||||
* @param mixed $fallback
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key = null, $fallback = null)
|
||||
{
|
||||
return A::get($this->data(), $key, $fallback);
|
||||
}
|
||||
|
||||
// convert to lowercase
|
||||
$key = strtolower($key);
|
||||
/**
|
||||
* 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');
|
||||
|
||||
// replace _ with spaces
|
||||
$key = str_replace('_', ' ', $key);
|
||||
return $header !== null;
|
||||
}
|
||||
|
||||
// uppercase first char in each word
|
||||
$key = ucwords($key);
|
||||
/**
|
||||
* Returns a header by key if it exists
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $fallback
|
||||
* @return mixed
|
||||
*/
|
||||
public function header(string $key, $fallback = null)
|
||||
{
|
||||
$headers = array_change_key_case($this->headers());
|
||||
return $headers[strtolower($key)] ?? $fallback;
|
||||
}
|
||||
|
||||
// convert spaces to dashes
|
||||
$key = str_replace(' ', '-', $key);
|
||||
/**
|
||||
* Return all headers with polyfill for
|
||||
* missing getallheaders function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function headers(): array
|
||||
{
|
||||
$headers = [];
|
||||
|
||||
$headers[$key] = $value;
|
||||
}
|
||||
foreach (Environment::getGlobally() as $key => $value) {
|
||||
if (substr($key, 0, 5) !== 'HTTP_' && substr($key, 0, 14) !== 'REDIRECT_HTTP_') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
// remove HTTP_
|
||||
$key = str_replace(['REDIRECT_HTTP_', 'HTTP_'], '', $key);
|
||||
|
||||
/**
|
||||
* Checks if the given method name
|
||||
* matches the name of the request method.
|
||||
*
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function is(string $method): bool
|
||||
{
|
||||
return strtoupper($this->method) === strtoupper($method);
|
||||
}
|
||||
// convert to lowercase
|
||||
$key = strtolower($key);
|
||||
|
||||
/**
|
||||
* Returns the request method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function method(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
// replace _ with spaces
|
||||
$key = str_replace('_', ' ', $key);
|
||||
|
||||
/**
|
||||
* Shortcut to the Params object
|
||||
*/
|
||||
public function params()
|
||||
{
|
||||
return $this->url()->params();
|
||||
}
|
||||
// uppercase first char in each word
|
||||
$key = ucwords($key);
|
||||
|
||||
/**
|
||||
* Shortcut to the Path object
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->url()->path();
|
||||
}
|
||||
// convert spaces to dashes
|
||||
$key = str_replace(' ', '-', $key);
|
||||
|
||||
/**
|
||||
* Returns the Query object
|
||||
*
|
||||
* @return \Kirby\Http\Request\Query
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
return $this->query ??= new Query();
|
||||
}
|
||||
$headers[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for a valid SSL connection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function ssl(): bool
|
||||
{
|
||||
return $this->url()->scheme() === 'https';
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current Uri object.
|
||||
* If you pass props you can safely modify
|
||||
* the Url with new parameters without destroying
|
||||
* the original object.
|
||||
*
|
||||
* @param array $props
|
||||
* @return \Kirby\Http\Uri
|
||||
*/
|
||||
public function url(array $props = null)
|
||||
{
|
||||
if ($props !== null) {
|
||||
return $this->url()->clone($props);
|
||||
}
|
||||
/**
|
||||
* Checks if the given method name
|
||||
* matches the name of the request method.
|
||||
*
|
||||
* @param string $method
|
||||
* @return bool
|
||||
*/
|
||||
public function is(string $method): bool
|
||||
{
|
||||
return strtoupper($this->method) === strtoupper($method);
|
||||
}
|
||||
|
||||
return $this->url ??= Uri::current();
|
||||
}
|
||||
/**
|
||||
* Returns the request method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function method(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to the Params object
|
||||
*/
|
||||
public function params()
|
||||
{
|
||||
return $this->url()->params();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to the Path object
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->url()->path();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Query object
|
||||
*
|
||||
* @return \Kirby\Http\Request\Query
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
return $this->query ??= new Query();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for a valid SSL connection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function ssl(): bool
|
||||
{
|
||||
return $this->url()->scheme() === 'https';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current Uri object.
|
||||
* If you pass props you can safely modify
|
||||
* the Url with new parameters without destroying
|
||||
* the original object.
|
||||
*
|
||||
* @param array $props
|
||||
* @return \Kirby\Http\Uri
|
||||
*/
|
||||
public function url(array $props = null)
|
||||
{
|
||||
if ($props !== null) {
|
||||
return $this->url()->clone($props);
|
||||
}
|
||||
|
||||
return $this->url ??= Uri::current();
|
||||
}
|
||||
}
|
||||
|
|
61
kirby/src/Http/Request/Auth.php
Normal file
61
kirby/src/Http/Request/Auth.php
Normal 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;
|
||||
}
|
|
@ -2,77 +2,84 @@
|
|||
|
||||
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
|
||||
*/
|
||||
protected $credentials;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $credentials;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
*/
|
||||
public function __construct(string $token)
|
||||
{
|
||||
parent::__construct($token);
|
||||
/**
|
||||
* @param string $token
|
||||
*/
|
||||
public function __construct(string $data)
|
||||
{
|
||||
parent::__construct($data);
|
||||
|
||||
$this->credentials = base64_decode($token);
|
||||
$this->username = Str::before($this->credentials, ':');
|
||||
$this->password = Str::after($this->credentials, ':');
|
||||
}
|
||||
$this->credentials = base64_decode($data);
|
||||
$this->username = Str::before($this->credentials, ':');
|
||||
$this->password = Str::after($this->credentials, ':');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entire unencoded credentials string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function credentials(): string
|
||||
{
|
||||
return $this->credentials;
|
||||
}
|
||||
/**
|
||||
* Returns the entire unencoded credentials string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function credentials(): string
|
||||
{
|
||||
return $this->credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function password(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
/**
|
||||
* Returns the password
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function password(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return 'basic';
|
||||
}
|
||||
/**
|
||||
* Returns the authentication type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return 'basic';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function username(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
/**
|
||||
* Returns the username
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function username(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,53 +2,36 @@
|
|||
|
||||
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;
|
||||
/**
|
||||
* Returns the authentication token
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function token(): string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function token(): string
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the auth type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return 'bearer';
|
||||
}
|
||||
/**
|
||||
* Returns the auth type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return 'bearer';
|
||||
}
|
||||
}
|
||||
|
|
48
kirby/src/Http/Request/Auth/SessionAuth.php
Normal file
48
kirby/src/Http/Request/Auth/SessionAuth.php
Normal 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';
|
||||
}
|
||||
}
|
|
@ -16,114 +16,114 @@ namespace Kirby\Http\Request;
|
|||
*/
|
||||
class Body
|
||||
{
|
||||
use Data;
|
||||
use Data;
|
||||
|
||||
/**
|
||||
* The raw body content
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
protected $contents;
|
||||
/**
|
||||
* The raw body content
|
||||
*
|
||||
* @var string|array
|
||||
*/
|
||||
protected $contents;
|
||||
|
||||
/**
|
||||
* The parsed content as array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
/**
|
||||
* The parsed content as array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Creates a new request body object.
|
||||
* You can pass your own array or string.
|
||||
* If null is being passed, the class will
|
||||
* fetch the body either from the $_POST global
|
||||
* or from php://input.
|
||||
*
|
||||
* @param array|string|null $contents
|
||||
*/
|
||||
public function __construct($contents = null)
|
||||
{
|
||||
$this->contents = $contents;
|
||||
}
|
||||
/**
|
||||
* Creates a new request body object.
|
||||
* You can pass your own array or string.
|
||||
* If null is being passed, the class will
|
||||
* fetch the body either from the $_POST global
|
||||
* or from php://input.
|
||||
*
|
||||
* @param array|string|null $contents
|
||||
*/
|
||||
public function __construct($contents = null)
|
||||
{
|
||||
$this->contents = $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the raw contents for the body
|
||||
* or uses the passed contents.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function contents()
|
||||
{
|
||||
if ($this->contents === null) {
|
||||
if (empty($_POST) === false) {
|
||||
$this->contents = $_POST;
|
||||
} else {
|
||||
$this->contents = file_get_contents('php://input');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Fetches the raw contents for the body
|
||||
* or uses the passed contents.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function contents()
|
||||
{
|
||||
if ($this->contents === null) {
|
||||
if (empty($_POST) === false) {
|
||||
$this->contents = $_POST;
|
||||
} else {
|
||||
$this->contents = file_get_contents('php://input');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->contents;
|
||||
}
|
||||
return $this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the raw contents once and caches
|
||||
* the result. The parser will try to convert
|
||||
* the body with the json decoder first and
|
||||
* then run parse_str to get some results
|
||||
* if the json decoder failed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
if (is_array($this->data) === true) {
|
||||
return $this->data;
|
||||
}
|
||||
/**
|
||||
* Parses the raw contents once and caches
|
||||
* the result. The parser will try to convert
|
||||
* the body with the json decoder first and
|
||||
* then run parse_str to get some results
|
||||
* if the json decoder failed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
if (is_array($this->data) === true) {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
$contents = $this->contents();
|
||||
$contents = $this->contents();
|
||||
|
||||
// return content which is already in array form
|
||||
if (is_array($contents) === true) {
|
||||
return $this->data = $contents;
|
||||
}
|
||||
// return content which is already in array form
|
||||
if (is_array($contents) === true) {
|
||||
return $this->data = $contents;
|
||||
}
|
||||
|
||||
// try to convert the body from json
|
||||
$json = json_decode($contents, true);
|
||||
// try to convert the body from json
|
||||
$json = json_decode($contents, true);
|
||||
|
||||
if (is_array($json) === true) {
|
||||
return $this->data = $json;
|
||||
}
|
||||
if (is_array($json) === true) {
|
||||
return $this->data = $json;
|
||||
}
|
||||
|
||||
if (strstr($contents, '=') !== false) {
|
||||
// try to parse the body as query string
|
||||
parse_str($contents, $parsed);
|
||||
if (strstr($contents, '=') !== false) {
|
||||
// try to parse the body as query string
|
||||
parse_str($contents, $parsed);
|
||||
|
||||
if (is_array($parsed)) {
|
||||
return $this->data = $parsed;
|
||||
}
|
||||
}
|
||||
if (is_array($parsed)) {
|
||||
return $this->data = $parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->data = [];
|
||||
}
|
||||
return $this->data = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the data array back
|
||||
* to a http query string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return http_build_query($this->data());
|
||||
}
|
||||
/**
|
||||
* Converts the data array back
|
||||
* to a http query string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return http_build_query($this->data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic string converter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
/**
|
||||
* Magic string converter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,67 +18,67 @@ namespace Kirby\Http\Request;
|
|||
*/
|
||||
trait Data
|
||||
{
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* The data provider method has to be
|
||||
* implemented by each class using this Trait
|
||||
* and has to return an associative array
|
||||
* for the get method
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function data(): array;
|
||||
/**
|
||||
* The data provider method has to be
|
||||
* implemented by each class using this Trait
|
||||
* and has to return an associative array
|
||||
* for the get method
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function data(): array;
|
||||
|
||||
/**
|
||||
* The get method is the heart and soul of this
|
||||
* Trait. You can use it to fetch a single value
|
||||
* of the data array by key or multiple values by
|
||||
* passing an array of keys.
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param mixed|null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if (is_array($key) === true) {
|
||||
$result = [];
|
||||
foreach ($key as $k) {
|
||||
$result[$k] = $this->get($k);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* The get method is the heart and soul of this
|
||||
* Trait. You can use it to fetch a single value
|
||||
* of the data array by key or multiple values by
|
||||
* passing an array of keys.
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param mixed|null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if (is_array($key) === true) {
|
||||
$result = [];
|
||||
foreach ($key as $k) {
|
||||
$result[$k] = $this->get($k);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->data()[$key] ?? $default;
|
||||
}
|
||||
return $this->data()[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data array.
|
||||
* This is basically an alias for Data::data()
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->data();
|
||||
}
|
||||
/**
|
||||
* Returns the data array.
|
||||
* This is basically an alias for Data::data()
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the data array to json
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJson(): string
|
||||
{
|
||||
return json_encode($this->data());
|
||||
}
|
||||
/**
|
||||
* Converts the data array to json
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJson(): string
|
||||
{
|
||||
return json_encode($this->data());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,57 +17,57 @@ namespace Kirby\Http\Request;
|
|||
*/
|
||||
class Files
|
||||
{
|
||||
use Data;
|
||||
use Data;
|
||||
|
||||
/**
|
||||
* Sanitized array of all received files
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $files;
|
||||
/**
|
||||
* Sanitized array of all received files
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $files;
|
||||
|
||||
/**
|
||||
* Creates a new Files object
|
||||
* Pass your own array to mock
|
||||
* uploads.
|
||||
*
|
||||
* @param array|null $files
|
||||
*/
|
||||
public function __construct($files = null)
|
||||
{
|
||||
if ($files === null) {
|
||||
$files = $_FILES;
|
||||
}
|
||||
/**
|
||||
* Creates a new Files object
|
||||
* Pass your own array to mock
|
||||
* uploads.
|
||||
*
|
||||
* @param array|null $files
|
||||
*/
|
||||
public function __construct($files = null)
|
||||
{
|
||||
if ($files === null) {
|
||||
$files = $_FILES;
|
||||
}
|
||||
|
||||
$this->files = [];
|
||||
$this->files = [];
|
||||
|
||||
foreach ($files as $key => $file) {
|
||||
if (is_array($file['name'])) {
|
||||
foreach ($file['name'] as $i => $name) {
|
||||
$this->files[$key][] = [
|
||||
'name' => $file['name'][$i] ?? null,
|
||||
'type' => $file['type'][$i] ?? null,
|
||||
'tmp_name' => $file['tmp_name'][$i] ?? null,
|
||||
'error' => $file['error'][$i] ?? null,
|
||||
'size' => $file['size'][$i] ?? null,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$this->files[$key] = $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($files as $key => $file) {
|
||||
if (is_array($file['name'])) {
|
||||
foreach ($file['name'] as $i => $name) {
|
||||
$this->files[$key][] = [
|
||||
'name' => $file['name'][$i] ?? null,
|
||||
'type' => $file['type'][$i] ?? null,
|
||||
'tmp_name' => $file['tmp_name'][$i] ?? null,
|
||||
'error' => $file['error'][$i] ?? null,
|
||||
'size' => $file['size'][$i] ?? null,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$this->files[$key] = $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The data method returns the files
|
||||
* array. This is only needed to make
|
||||
* the Data trait work for the Files::get($key)
|
||||
* method.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
/**
|
||||
* The data method returns the files
|
||||
* array. This is only needed to make
|
||||
* the Data trait work for the Files::get($key)
|
||||
* method.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,84 +15,84 @@ namespace Kirby\Http\Request;
|
|||
*/
|
||||
class Query
|
||||
{
|
||||
use Data;
|
||||
use Data;
|
||||
|
||||
/**
|
||||
* The Query data array
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $data;
|
||||
/**
|
||||
* The Query data array
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Creates a new Query object.
|
||||
* The passed data can be an array
|
||||
* or a parsable query string. If
|
||||
* null is passed, the current Query
|
||||
* will be taken from $_GET
|
||||
*
|
||||
* @param array|string|null $data
|
||||
*/
|
||||
public function __construct($data = null)
|
||||
{
|
||||
if ($data === null) {
|
||||
$this->data = $_GET;
|
||||
} elseif (is_array($data)) {
|
||||
$this->data = $data;
|
||||
} else {
|
||||
parse_str($data, $parsed);
|
||||
$this->data = $parsed;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a new Query object.
|
||||
* The passed data can be an array
|
||||
* or a parsable query string. If
|
||||
* null is passed, the current Query
|
||||
* will be taken from $_GET
|
||||
*
|
||||
* @param array|string|null $data
|
||||
*/
|
||||
public function __construct($data = null)
|
||||
{
|
||||
if ($data === null) {
|
||||
$this->data = $_GET;
|
||||
} elseif (is_array($data)) {
|
||||
$this->data = $data;
|
||||
} else {
|
||||
parse_str($data, $parsed);
|
||||
$this->data = $parsed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Query data as array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
/**
|
||||
* Returns the Query data as array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the request doesn't contain query variables
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->data) === true;
|
||||
}
|
||||
/**
|
||||
* Returns `true` if the request doesn't contain query variables
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->data) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the request contains query variables
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return empty($this->data) === false;
|
||||
}
|
||||
/**
|
||||
* Returns `true` if the request contains query variables
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEmpty(): bool
|
||||
{
|
||||
return empty($this->data) === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the query data array
|
||||
* back to a query string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return http_build_query($this->data());
|
||||
}
|
||||
/**
|
||||
* Converts the query data array
|
||||
* back to a query string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return http_build_query($this->data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic string converter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
/**
|
||||
* Magic string converter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,299 +19,316 @@ use Throwable;
|
|||
*/
|
||||
class Response
|
||||
{
|
||||
/**
|
||||
* Store for all registered headers,
|
||||
* which will be sent with the response
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [];
|
||||
/**
|
||||
* Store for all registered headers,
|
||||
* which will be sent with the response
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [];
|
||||
|
||||
/**
|
||||
* The response body
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $body;
|
||||
/**
|
||||
* The response body
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $body;
|
||||
|
||||
/**
|
||||
* The HTTP response code
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $code;
|
||||
/**
|
||||
* The HTTP response code
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* The content type for the response
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
/**
|
||||
* The content type for the response
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The content type charset
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $charset = 'UTF-8';
|
||||
/**
|
||||
* The content type charset
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $charset = 'UTF-8';
|
||||
|
||||
/**
|
||||
* Creates a new response object
|
||||
*
|
||||
* @param string $body
|
||||
* @param string $type
|
||||
* @param int $code
|
||||
* @param array $headers
|
||||
* @param string $charset
|
||||
*/
|
||||
public function __construct($body = '', ?string $type = null, ?int $code = null, ?array $headers = null, ?string $charset = null)
|
||||
{
|
||||
// array construction
|
||||
if (is_array($body) === true) {
|
||||
$params = $body;
|
||||
$body = $params['body'] ?? '';
|
||||
$type = $params['type'] ?? $type;
|
||||
$code = $params['code'] ?? $code;
|
||||
$headers = $params['headers'] ?? $headers;
|
||||
$charset = $params['charset'] ?? $charset;
|
||||
}
|
||||
/**
|
||||
* Creates a new response object
|
||||
*
|
||||
* @param string $body
|
||||
* @param string $type
|
||||
* @param int $code
|
||||
* @param array $headers
|
||||
* @param string $charset
|
||||
*/
|
||||
public function __construct($body = '', ?string $type = null, ?int $code = null, ?array $headers = null, ?string $charset = null)
|
||||
{
|
||||
// array construction
|
||||
if (is_array($body) === true) {
|
||||
$params = $body;
|
||||
$body = $params['body'] ?? '';
|
||||
$type = $params['type'] ?? $type;
|
||||
$code = $params['code'] ?? $code;
|
||||
$headers = $params['headers'] ?? $headers;
|
||||
$charset = $params['charset'] ?? $charset;
|
||||
}
|
||||
|
||||
// regular construction
|
||||
$this->body = $body;
|
||||
$this->type = $type ?? 'text/html';
|
||||
$this->code = $code ?? 200;
|
||||
$this->headers = $headers ?? [];
|
||||
$this->charset = $charset ?? 'UTF-8';
|
||||
// regular construction
|
||||
$this->body = $body;
|
||||
$this->type = $type ?? 'text/html';
|
||||
$this->code = $code ?? 200;
|
||||
$this->headers = $headers ?? [];
|
||||
$this->charset = $charset ?? 'UTF-8';
|
||||
|
||||
// automatic mime type detection
|
||||
if (strpos($this->type, '/') === false) {
|
||||
$this->type = F::extensionToMime($this->type) ?? 'text/html';
|
||||
}
|
||||
}
|
||||
// automatic mime type detection
|
||||
if (strpos($this->type, '/') === false) {
|
||||
$this->type = F::extensionToMime($this->type) ?? 'text/html';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes it possible to convert the
|
||||
* entire response object to a string
|
||||
* to send the headers and print the body
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
return $this->send();
|
||||
} catch (Throwable $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Makes it possible to convert the
|
||||
* entire response object to a string
|
||||
* to send the headers and print the body
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
return $this->send();
|
||||
} catch (Throwable $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the body
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function body(): string
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
/**
|
||||
* Getter for the body
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function body(): string
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the content type charset
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function charset(): string
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
/**
|
||||
* Getter for the content type charset
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function charset(): string
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the HTTP status code
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function code(): int
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
/**
|
||||
* Getter for the HTTP status code
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function code(): int
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a response that triggers
|
||||
* a file download for the given file
|
||||
*
|
||||
* @param string $file
|
||||
* @param string $filename
|
||||
* @param array $props Custom overrides for response props (e.g. headers)
|
||||
* @return static
|
||||
*/
|
||||
public static function download(string $file, string $filename = null, array $props = [])
|
||||
{
|
||||
if (file_exists($file) === false) {
|
||||
throw new Exception('The file could not be found');
|
||||
}
|
||||
/**
|
||||
* Creates a response that triggers
|
||||
* a file download for the given file
|
||||
*
|
||||
* @param string $file
|
||||
* @param string $filename
|
||||
* @param array $props Custom overrides for response props (e.g. headers)
|
||||
* @return static
|
||||
*/
|
||||
public static function download(string $file, string $filename = null, array $props = [])
|
||||
{
|
||||
if (file_exists($file) === false) {
|
||||
throw new Exception('The file could not be found');
|
||||
}
|
||||
|
||||
$filename ??= basename($file);
|
||||
$modified = filemtime($file);
|
||||
$body = file_get_contents($file);
|
||||
$size = strlen($body);
|
||||
$filename ??= basename($file);
|
||||
$modified = filemtime($file);
|
||||
$body = file_get_contents($file);
|
||||
$size = strlen($body);
|
||||
|
||||
$props = array_replace_recursive([
|
||||
'body' => $body,
|
||||
'type' => F::mime($file),
|
||||
'headers' => [
|
||||
'Pragma' => 'public',
|
||||
'Cache-Control' => 'no-cache, no-store, must-revalidate',
|
||||
'Last-Modified' => gmdate('D, d M Y H:i:s', $modified) . ' GMT',
|
||||
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
||||
'Content-Transfer-Encoding' => 'binary',
|
||||
'Content-Length' => $size,
|
||||
'Connection' => 'close'
|
||||
]
|
||||
], $props);
|
||||
$props = array_replace_recursive([
|
||||
'body' => $body,
|
||||
'type' => F::mime($file),
|
||||
'headers' => [
|
||||
'Pragma' => 'public',
|
||||
'Cache-Control' => 'no-cache, no-store, must-revalidate',
|
||||
'Last-Modified' => gmdate('D, d M Y H:i:s', $modified) . ' GMT',
|
||||
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
||||
'Content-Transfer-Encoding' => 'binary',
|
||||
'Content-Length' => $size,
|
||||
'Connection' => 'close'
|
||||
]
|
||||
], $props);
|
||||
|
||||
return new static($props);
|
||||
}
|
||||
return new static($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a response for a file and
|
||||
* sends the file content to the browser
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $props Custom overrides for response props (e.g. headers)
|
||||
* @return static
|
||||
*/
|
||||
public static function file(string $file, array $props = [])
|
||||
{
|
||||
$props = array_merge([
|
||||
'body' => F::read($file),
|
||||
'type' => F::extensionToMime(F::extension($file))
|
||||
], $props);
|
||||
/**
|
||||
* Creates a response for a file and
|
||||
* sends the file content to the browser
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $props Custom overrides for response props (e.g. headers)
|
||||
* @return static
|
||||
*/
|
||||
public static function file(string $file, array $props = [])
|
||||
{
|
||||
$props = array_merge([
|
||||
'body' => F::read($file),
|
||||
'type' => F::extensionToMime(F::extension($file))
|
||||
], $props);
|
||||
|
||||
return new static($props);
|
||||
}
|
||||
return new static($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for single headers
|
||||
*
|
||||
* @param string $key Name of the header
|
||||
* @return string|null
|
||||
*/
|
||||
public function header(string $key): ?string
|
||||
{
|
||||
return $this->headers[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for all headers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function headers(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a json response with appropriate
|
||||
* header and automatic conversion of arrays.
|
||||
*
|
||||
* @param string|array $body
|
||||
* @param int $code
|
||||
* @param bool $pretty
|
||||
* @param array $headers
|
||||
* @return static
|
||||
*/
|
||||
public static function json($body = '', ?int $code = null, ?bool $pretty = null, array $headers = [])
|
||||
{
|
||||
if (is_array($body) === true) {
|
||||
$body = json_encode($body, $pretty === true ? JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES : 0);
|
||||
}
|
||||
/**
|
||||
* Getter for single headers
|
||||
*
|
||||
* @param string $key Name of the header
|
||||
* @return string|null
|
||||
*/
|
||||
public function header(string $key): ?string
|
||||
{
|
||||
return $this->headers[$key] ?? null;
|
||||
}
|
||||
|
||||
return new static([
|
||||
'body' => $body,
|
||||
'code' => $code,
|
||||
'type' => 'application/json',
|
||||
'headers' => $headers
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Getter for all headers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function headers(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a redirect response,
|
||||
* which will send the visitor to the
|
||||
* given location.
|
||||
*
|
||||
* @param string $location
|
||||
* @param int $code
|
||||
* @return static
|
||||
*/
|
||||
public static function redirect(string $location = '/', int $code = 302)
|
||||
{
|
||||
return new static([
|
||||
'code' => $code,
|
||||
'headers' => [
|
||||
'Location' => Url::unIdn($location)
|
||||
]
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Creates a json response with appropriate
|
||||
* header and automatic conversion of arrays.
|
||||
*
|
||||
* @param string|array $body
|
||||
* @param int $code
|
||||
* @param bool $pretty
|
||||
* @param array $headers
|
||||
* @return static
|
||||
*/
|
||||
public static function json($body = '', ?int $code = null, ?bool $pretty = null, array $headers = [])
|
||||
{
|
||||
if (is_array($body) === true) {
|
||||
$body = json_encode($body, $pretty === true ? JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends all registered headers and
|
||||
* returns the response body
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function send(): string
|
||||
{
|
||||
// send the status response code
|
||||
http_response_code($this->code());
|
||||
return new static([
|
||||
'body' => $body,
|
||||
'code' => $code,
|
||||
'type' => 'application/json',
|
||||
'headers' => $headers
|
||||
]);
|
||||
}
|
||||
|
||||
// send all custom headers
|
||||
foreach ($this->headers() as $key => $value) {
|
||||
header($key . ': ' . $value);
|
||||
}
|
||||
/**
|
||||
* Creates a redirect response,
|
||||
* which will send the visitor to the
|
||||
* given location.
|
||||
*
|
||||
* @param string $location
|
||||
* @param int $code
|
||||
* @return static
|
||||
*/
|
||||
public static function redirect(string $location = '/', int $code = 302)
|
||||
{
|
||||
return new static([
|
||||
'code' => $code,
|
||||
'headers' => [
|
||||
'Location' => Url::unIdn($location)
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
// send the content type header
|
||||
header('Content-Type:' . $this->type() . '; charset=' . $this->charset());
|
||||
/**
|
||||
* Sends all registered headers and
|
||||
* returns the response body
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function send(): string
|
||||
{
|
||||
// send the status response code
|
||||
http_response_code($this->code());
|
||||
|
||||
// print the response body
|
||||
return $this->body();
|
||||
}
|
||||
// send all custom headers
|
||||
foreach ($this->headers() as $key => $value) {
|
||||
header($key . ': ' . $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all relevant response attributes
|
||||
* to an associative array for debugging,
|
||||
* testing or whatever.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'type' => $this->type(),
|
||||
'charset' => $this->charset(),
|
||||
'code' => $this->code(),
|
||||
'headers' => $this->headers(),
|
||||
'body' => $this->body()
|
||||
];
|
||||
}
|
||||
// send the content type header
|
||||
header('Content-Type:' . $this->type() . '; charset=' . $this->charset());
|
||||
|
||||
/**
|
||||
* Getter for the content type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
// print the response body
|
||||
return $this->body();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all relevant response attributes
|
||||
* to an associative array for debugging,
|
||||
* testing or whatever.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'type' => $this->type(),
|
||||
'charset' => $this->charset(),
|
||||
'code' => $this->code(),
|
||||
'headers' => $this->headers(),
|
||||
'body' => $this->body()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the content type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function type(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,218 +13,218 @@ use Closure;
|
|||
*/
|
||||
class Route
|
||||
{
|
||||
/**
|
||||
* The callback action function
|
||||
*
|
||||
* @var Closure
|
||||
*/
|
||||
protected $action;
|
||||
/**
|
||||
* The callback action function
|
||||
*
|
||||
* @var Closure
|
||||
*/
|
||||
protected $action;
|
||||
|
||||
/**
|
||||
* Listed of parsed arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
/**
|
||||
* Listed of parsed arguments
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* An array of all passed attributes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
/**
|
||||
* An array of all passed attributes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* The registered request method
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $method;
|
||||
/**
|
||||
* The registered request method
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $method;
|
||||
|
||||
/**
|
||||
* The registered pattern
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pattern;
|
||||
/**
|
||||
* The registered pattern
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pattern;
|
||||
|
||||
/**
|
||||
* Wildcards, which can be used in
|
||||
* Route patterns to make regular expressions
|
||||
* a little more human
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $wildcards = [
|
||||
'required' => [
|
||||
'(:num)' => '(-?[0-9]+)',
|
||||
'(:alpha)' => '([a-zA-Z]+)',
|
||||
'(:alphanum)' => '([a-zA-Z0-9]+)',
|
||||
'(:any)' => '([a-zA-Z0-9\.\-_%= \+\@\(\)]+)',
|
||||
'(:all)' => '(.*)',
|
||||
],
|
||||
'optional' => [
|
||||
'/(:num?)' => '(?:/(-?[0-9]+)',
|
||||
'/(:alpha?)' => '(?:/([a-zA-Z]+)',
|
||||
'/(:alphanum?)' => '(?:/([a-zA-Z0-9]+)',
|
||||
'/(:any?)' => '(?:/([a-zA-Z0-9\.\-_%= \+\@\(\)]+)',
|
||||
'/(:all?)' => '(?:/(.*)',
|
||||
],
|
||||
];
|
||||
/**
|
||||
* Wildcards, which can be used in
|
||||
* Route patterns to make regular expressions
|
||||
* a little more human
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $wildcards = [
|
||||
'required' => [
|
||||
'(:num)' => '(-?[0-9]+)',
|
||||
'(:alpha)' => '([a-zA-Z]+)',
|
||||
'(:alphanum)' => '([a-zA-Z0-9]+)',
|
||||
'(:any)' => '([a-zA-Z0-9\.\-_%= \+\@\(\)]+)',
|
||||
'(:all)' => '(.*)',
|
||||
],
|
||||
'optional' => [
|
||||
'/(:num?)' => '(?:/(-?[0-9]+)',
|
||||
'/(:alpha?)' => '(?:/([a-zA-Z]+)',
|
||||
'/(:alphanum?)' => '(?:/([a-zA-Z0-9]+)',
|
||||
'/(:any?)' => '(?:/([a-zA-Z0-9\.\-_%= \+\@\(\)]+)',
|
||||
'/(:all?)' => '(?:/(.*)',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Magic getter for route attributes
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $key, array $arguments = null)
|
||||
{
|
||||
return $this->attributes[$key] ?? null;
|
||||
}
|
||||
/**
|
||||
* Magic getter for route attributes
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $key, array $arguments = null)
|
||||
{
|
||||
return $this->attributes[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Route object for the given
|
||||
* pattern(s), method(s) and the callback action
|
||||
*
|
||||
* @param string|array $pattern
|
||||
* @param string|array $method
|
||||
* @param Closure $action
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($pattern, $method, Closure $action, array $attributes = [])
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->attributes = $attributes;
|
||||
$this->method = $method;
|
||||
$this->pattern = $this->regex(ltrim($pattern, '/'));
|
||||
}
|
||||
/**
|
||||
* Creates a new Route object for the given
|
||||
* pattern(s), method(s) and the callback action
|
||||
*
|
||||
* @param string|array $pattern
|
||||
* @param string|array $method
|
||||
* @param Closure $action
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function __construct($pattern, $method, Closure $action, array $attributes = [])
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->attributes = $attributes;
|
||||
$this->method = $method;
|
||||
$this->pattern = $this->regex(ltrim($pattern, '/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the action callback
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
public function action()
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
/**
|
||||
* Getter for the action callback
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
public function action()
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all parsed arguments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function arguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
/**
|
||||
* Returns all parsed arguments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function arguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for additional attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
/**
|
||||
* Getter for additional attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function method(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
/**
|
||||
* Getter for the method
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function method(): string
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route name if set
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function name(): ?string
|
||||
{
|
||||
return $this->attributes['name'] ?? null;
|
||||
}
|
||||
/**
|
||||
* Returns the route name if set
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function name(): ?string
|
||||
{
|
||||
return $this->attributes['name'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a specific exception to tell
|
||||
* the router to jump to the next route
|
||||
* @since 3.0.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function next(): void
|
||||
{
|
||||
throw new Exceptions\NextRouteException('next');
|
||||
}
|
||||
/**
|
||||
* Throws a specific exception to tell
|
||||
* the router to jump to the next route
|
||||
* @since 3.0.3
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function next(): void
|
||||
{
|
||||
throw new Exceptions\NextRouteException('next');
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the pattern
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function pattern(): string
|
||||
{
|
||||
return $this->pattern;
|
||||
}
|
||||
/**
|
||||
* Getter for the pattern
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function pattern(): string
|
||||
{
|
||||
return $this->pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the pattern into a full regular
|
||||
* expression by replacing all the wildcards
|
||||
*
|
||||
* @param string $pattern
|
||||
* @return string
|
||||
*/
|
||||
public function regex(string $pattern): string
|
||||
{
|
||||
$search = array_keys($this->wildcards['optional']);
|
||||
$replace = array_values($this->wildcards['optional']);
|
||||
/**
|
||||
* Converts the pattern into a full regular
|
||||
* expression by replacing all the wildcards
|
||||
*
|
||||
* @param string $pattern
|
||||
* @return string
|
||||
*/
|
||||
public function regex(string $pattern): string
|
||||
{
|
||||
$search = array_keys($this->wildcards['optional']);
|
||||
$replace = array_values($this->wildcards['optional']);
|
||||
|
||||
// For optional parameters, first translate the wildcards to their
|
||||
// regex equivalent, sans the ")?" ending. We'll add the endings
|
||||
// back on when we know the replacement count.
|
||||
$pattern = str_replace($search, $replace, $pattern, $count);
|
||||
// For optional parameters, first translate the wildcards to their
|
||||
// regex equivalent, sans the ")?" ending. We'll add the endings
|
||||
// back on when we know the replacement count.
|
||||
$pattern = str_replace($search, $replace, $pattern, $count);
|
||||
|
||||
if ($count > 0) {
|
||||
$pattern .= str_repeat(')?', $count);
|
||||
}
|
||||
if ($count > 0) {
|
||||
$pattern .= str_repeat(')?', $count);
|
||||
}
|
||||
|
||||
return strtr($pattern, $this->wildcards['required']);
|
||||
}
|
||||
return strtr($pattern, $this->wildcards['required']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to match the path with the regular expression and
|
||||
* extracts all arguments for the Route action
|
||||
*
|
||||
* @param string $pattern
|
||||
* @param string $path
|
||||
* @return array|false
|
||||
*/
|
||||
public function parse(string $pattern, string $path)
|
||||
{
|
||||
// check for direct matches
|
||||
if ($pattern === $path) {
|
||||
return $this->arguments = [];
|
||||
}
|
||||
/**
|
||||
* Tries to match the path with the regular expression and
|
||||
* extracts all arguments for the Route action
|
||||
*
|
||||
* @param string $pattern
|
||||
* @param string $path
|
||||
* @return array|false
|
||||
*/
|
||||
public function parse(string $pattern, string $path)
|
||||
{
|
||||
// check for direct matches
|
||||
if ($pattern === $path) {
|
||||
return $this->arguments = [];
|
||||
}
|
||||
|
||||
// We only need to check routes with regular expression since all others
|
||||
// would have been able to be matched by the search for literal matches
|
||||
// we just did before we started searching.
|
||||
if (strpos($pattern, '(') === false) {
|
||||
return false;
|
||||
}
|
||||
// We only need to check routes with regular expression since all others
|
||||
// would have been able to be matched by the search for literal matches
|
||||
// we just did before we started searching.
|
||||
if (strpos($pattern, '(') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have a match we'll return all results
|
||||
// from the preg without the full first match.
|
||||
if (preg_match('#^' . $this->regex($pattern) . '$#u', $path, $parameters)) {
|
||||
return $this->arguments = array_slice($parameters, 1);
|
||||
}
|
||||
// If we have a match we'll return all results
|
||||
// from the preg without the full first match.
|
||||
if (preg_match('#^' . $this->regex($pattern) . '$#u', $path, $parameters)) {
|
||||
return $this->arguments = array_slice($parameters, 1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,158 +16,194 @@ use Kirby\Toolkit\A;
|
|||
*/
|
||||
class Router
|
||||
{
|
||||
public static $beforeEach;
|
||||
public static $afterEach;
|
||||
/**
|
||||
* Hook that is called after each route
|
||||
*
|
||||
* @var \Closure
|
||||
*/
|
||||
protected $afterEach;
|
||||
|
||||
/**
|
||||
* Store for the current route,
|
||||
* if one can be found
|
||||
*
|
||||
* @var Route|null
|
||||
*/
|
||||
protected $route;
|
||||
/**
|
||||
* Hook that is called before each route
|
||||
*
|
||||
* @var \Closure
|
||||
*/
|
||||
protected $beforeEach;
|
||||
|
||||
/**
|
||||
* All registered routes, sorted by
|
||||
* their request method. This makes
|
||||
* it faster to find the right route
|
||||
* later.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routes = [
|
||||
'GET' => [],
|
||||
'HEAD' => [],
|
||||
'POST' => [],
|
||||
'PUT' => [],
|
||||
'DELETE' => [],
|
||||
'CONNECT' => [],
|
||||
'OPTIONS' => [],
|
||||
'TRACE' => [],
|
||||
'PATCH' => [],
|
||||
];
|
||||
/**
|
||||
* Store for the current route,
|
||||
* if one can be found
|
||||
*
|
||||
* @var \Kirby\Http\Route|null
|
||||
*/
|
||||
protected $route;
|
||||
|
||||
/**
|
||||
* Creates a new router object and
|
||||
* registers all the given routes
|
||||
*
|
||||
* @param array $routes
|
||||
*/
|
||||
public function __construct(array $routes = [])
|
||||
{
|
||||
foreach ($routes as $props) {
|
||||
if (isset($props['pattern'], $props['action']) === false) {
|
||||
throw new InvalidArgumentException('Invalid route parameters');
|
||||
}
|
||||
/**
|
||||
* All registered routes, sorted by
|
||||
* their request method. This makes
|
||||
* it faster to find the right route
|
||||
* later.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routes = [
|
||||
'GET' => [],
|
||||
'HEAD' => [],
|
||||
'POST' => [],
|
||||
'PUT' => [],
|
||||
'DELETE' => [],
|
||||
'CONNECT' => [],
|
||||
'OPTIONS' => [],
|
||||
'TRACE' => [],
|
||||
'PATCH' => [],
|
||||
];
|
||||
|
||||
$patterns = A::wrap($props['pattern']);
|
||||
$methods = A::map(
|
||||
explode('|', strtoupper($props['method'] ?? 'GET')),
|
||||
'trim'
|
||||
);
|
||||
/**
|
||||
* Creates a new router object and
|
||||
* registers all the given routes
|
||||
*
|
||||
* @param array $routes
|
||||
* @param array<string, \Closure> $hooks Optional `beforeEach` and `afterEach` hooks
|
||||
*/
|
||||
public function __construct(array $routes = [], array $hooks = [])
|
||||
{
|
||||
$this->beforeEach = $hooks['beforeEach'] ?? null;
|
||||
$this->afterEach = $hooks['afterEach'] ?? null;
|
||||
|
||||
if ($methods === ['ALL']) {
|
||||
$methods = array_keys($this->routes);
|
||||
}
|
||||
foreach ($routes as $props) {
|
||||
if (isset($props['pattern'], $props['action']) === false) {
|
||||
throw new InvalidArgumentException('Invalid route parameters');
|
||||
}
|
||||
|
||||
foreach ($methods as $method) {
|
||||
foreach ($patterns as $pattern) {
|
||||
$this->routes[$method][] = new Route($pattern, $method, $props['action'], $props);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$patterns = A::wrap($props['pattern']);
|
||||
$methods = A::map(
|
||||
explode('|', strtoupper($props['method'] ?? 'GET')),
|
||||
'trim'
|
||||
);
|
||||
|
||||
/**
|
||||
* Calls the Router by path and method.
|
||||
* This will try to find a Route object
|
||||
* and then call the Route action with
|
||||
* the appropriate arguments and a Result
|
||||
* object.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $method
|
||||
* @param Closure|null $callback
|
||||
* @return mixed
|
||||
*/
|
||||
public function call(string $path = null, string $method = 'GET', Closure $callback = null)
|
||||
{
|
||||
$path ??= '';
|
||||
$ignore = [];
|
||||
$result = null;
|
||||
$loop = true;
|
||||
if ($methods === ['ALL']) {
|
||||
$methods = array_keys($this->routes);
|
||||
}
|
||||
|
||||
while ($loop === true) {
|
||||
$route = $this->find($path, $method, $ignore);
|
||||
foreach ($methods as $method) {
|
||||
foreach ($patterns as $pattern) {
|
||||
$this->routes[$method][] = new Route(
|
||||
$pattern,
|
||||
$method,
|
||||
$props['action'],
|
||||
$props
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_a(static::$beforeEach, 'Closure') === true) {
|
||||
(static::$beforeEach)($route, $path, $method);
|
||||
}
|
||||
/**
|
||||
* Calls the Router by path and method.
|
||||
* This will try to find a Route object
|
||||
* and then call the Route action with
|
||||
* the appropriate arguments and a Result
|
||||
* object.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $method
|
||||
* @param Closure|null $callback
|
||||
* @return mixed
|
||||
*/
|
||||
public function call(string $path = null, string $method = 'GET', Closure $callback = null)
|
||||
{
|
||||
$path ??= '';
|
||||
$ignore = [];
|
||||
$result = null;
|
||||
$loop = true;
|
||||
|
||||
try {
|
||||
if ($callback) {
|
||||
$result = $callback($route);
|
||||
} else {
|
||||
$result = $route->action()->call($route, ...$route->arguments());
|
||||
}
|
||||
while ($loop === true) {
|
||||
$route = $this->find($path, $method, $ignore);
|
||||
|
||||
$loop = false;
|
||||
} catch (Exceptions\NextRouteException $e) {
|
||||
$ignore[] = $route;
|
||||
}
|
||||
if (is_a($this->beforeEach, 'Closure') === true) {
|
||||
($this->beforeEach)($route, $path, $method);
|
||||
}
|
||||
|
||||
if (is_a(static::$afterEach, 'Closure') === true) {
|
||||
$final = $loop === false;
|
||||
$result = (static::$afterEach)($route, $path, $method, $result, $final);
|
||||
}
|
||||
}
|
||||
try {
|
||||
if ($callback) {
|
||||
$result = $callback($route);
|
||||
} else {
|
||||
$result = $route->action()->call($route, ...$route->arguments());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
$loop = false;
|
||||
} catch (Exceptions\NextRouteException $e) {
|
||||
$ignore[] = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a Route object by path and method
|
||||
* The Route's arguments method is used to
|
||||
* find matches and return all the found
|
||||
* arguments in the path.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $method
|
||||
* @param array $ignore
|
||||
* @return \Kirby\Http\Route|null
|
||||
*/
|
||||
public function find(string $path, string $method, array $ignore = null)
|
||||
{
|
||||
if (isset($this->routes[$method]) === false) {
|
||||
throw new InvalidArgumentException('Invalid routing method: ' . $method, 400);
|
||||
}
|
||||
if (is_a($this->afterEach, 'Closure') === true) {
|
||||
$final = $loop === false;
|
||||
$result = ($this->afterEach)($route, $path, $method, $result, $final);
|
||||
}
|
||||
}
|
||||
|
||||
// remove leading and trailing slashes
|
||||
$path = trim($path, '/');
|
||||
return $result;
|
||||
}
|
||||
|
||||
foreach ($this->routes[$method] as $route) {
|
||||
$arguments = $route->parse($route->pattern(), $path);
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
if ($arguments !== false) {
|
||||
if (empty($ignore) === true || in_array($route, $ignore) === false) {
|
||||
return $this->route = $route;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Finds a Route object by path and method
|
||||
* The Route's arguments method is used to
|
||||
* find matches and return all the found
|
||||
* arguments in the path.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $method
|
||||
* @param array $ignore
|
||||
* @return \Kirby\Http\Route|null
|
||||
*/
|
||||
public function find(string $path, string $method, array $ignore = null)
|
||||
{
|
||||
if (isset($this->routes[$method]) === false) {
|
||||
throw new InvalidArgumentException('Invalid routing method: ' . $method, 400);
|
||||
}
|
||||
|
||||
throw new Exception('No route found for path: "' . $path . '" and request method: "' . $method . '"', 404);
|
||||
}
|
||||
// remove leading and trailing slashes
|
||||
$path = trim($path, '/');
|
||||
|
||||
/**
|
||||
* Returns the current route.
|
||||
* This will only return something,
|
||||
* once Router::find() has been called
|
||||
* and only if a route was found.
|
||||
*
|
||||
* @return \Kirby\Http\Route|null
|
||||
*/
|
||||
public function route()
|
||||
{
|
||||
return $this->route;
|
||||
}
|
||||
foreach ($this->routes[$method] as $route) {
|
||||
$arguments = $route->parse($route->pattern(), $path);
|
||||
|
||||
if ($arguments !== false) {
|
||||
if (empty($ignore) === true || in_array($route, $ignore) === false) {
|
||||
return $this->route = $route;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception('No route found for path: "' . $path . '" and request method: "' . $method . '"', 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current route.
|
||||
* This will only return something,
|
||||
* once Router::find() has been called
|
||||
* and only if a route was found.
|
||||
*
|
||||
* @return \Kirby\Http\Route|null
|
||||
*/
|
||||
public function route()
|
||||
{
|
||||
return $this->route;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
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 $cli;
|
||||
public static $hosts;
|
||||
|
||||
/**
|
||||
* List of trusted hosts
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $hosts = [];
|
||||
|
||||
/**
|
||||
* Returns the server's IP address
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function address(): string
|
||||
{
|
||||
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 \Kirby\Http\Environment
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
return new Environment([
|
||||
'cli' => static::$cli,
|
||||
'allowed' => static::$hosts
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,276 +15,275 @@ use Kirby\Toolkit\Str;
|
|||
*/
|
||||
class Url
|
||||
{
|
||||
/**
|
||||
* The base Url to build absolute Urls from
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $home = '/';
|
||||
/**
|
||||
* The base Url to build absolute Urls from
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $home = '/';
|
||||
|
||||
/**
|
||||
* The current Uri object
|
||||
*
|
||||
* @var Uri
|
||||
*/
|
||||
public static $current = null;
|
||||
/**
|
||||
* The current Uri object
|
||||
*
|
||||
* @var Uri
|
||||
*/
|
||||
public static $current = null;
|
||||
|
||||
/**
|
||||
* Facade for all Uri object methods
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic(string $method, $arguments)
|
||||
{
|
||||
return (new Uri($arguments[0] ?? static::current()))->$method(...array_slice($arguments, 1));
|
||||
}
|
||||
/**
|
||||
* Facade for all Uri object methods
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic(string $method, $arguments)
|
||||
{
|
||||
return (new Uri($arguments[0] ?? static::current()))->$method(...array_slice($arguments, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Url Builder
|
||||
* Actually just a factory for `new Uri($parts)`
|
||||
*
|
||||
* @param array $parts
|
||||
* @param string|null $url
|
||||
* @return string
|
||||
*/
|
||||
public static function build(array $parts = [], string $url = null): string
|
||||
{
|
||||
return (string)(new Uri($url ?? static::current()))->clone($parts);
|
||||
}
|
||||
/**
|
||||
* Url Builder
|
||||
* Actually just a factory for `new Uri($parts)`
|
||||
*
|
||||
* @param array $parts
|
||||
* @param string|null $url
|
||||
* @return string
|
||||
*/
|
||||
public static function build(array $parts = [], string $url = null): string
|
||||
{
|
||||
return (string)(new Uri($url ?? static::current()))->clone($parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current url with all bells and whistles
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function current(): string
|
||||
{
|
||||
return static::$current = static::$current ?? static::toObject()->toString();
|
||||
}
|
||||
/**
|
||||
* Returns the current url with all bells and whistles
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function current(): string
|
||||
{
|
||||
return static::$current = static::$current ?? static::toObject()->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the url for the current directory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function currentDir(): string
|
||||
{
|
||||
return dirname(static::current());
|
||||
}
|
||||
/**
|
||||
* Returns the url for the current directory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function currentDir(): string
|
||||
{
|
||||
return dirname(static::current());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to fix a broken url without protocol
|
||||
*
|
||||
* @param string|null $url
|
||||
* @return string
|
||||
*/
|
||||
public static function fix(string $url = null): string
|
||||
{
|
||||
// make sure to not touch absolute urls
|
||||
return (!preg_match('!^(https|http|ftp)\:\/\/!i', $url ?? '')) ? 'http://' . $url : $url;
|
||||
}
|
||||
/**
|
||||
* Tries to fix a broken url without protocol
|
||||
*
|
||||
* @param string|null $url
|
||||
* @return string
|
||||
*/
|
||||
public static function fix(string $url = null): string
|
||||
{
|
||||
// make sure to not touch absolute urls
|
||||
return (!preg_match('!^(https|http|ftp)\:\/\/!i', $url ?? '')) ? 'http://' . $url : $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the home url if defined
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function home(): string
|
||||
{
|
||||
return static::$home;
|
||||
}
|
||||
/**
|
||||
* Returns the home url if defined
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function home(): string
|
||||
{
|
||||
return static::$home;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
return Uri::index($props)->toString();
|
||||
}
|
||||
/**
|
||||
* Returns the url to the executed script
|
||||
*
|
||||
* @param array $props
|
||||
* @return string
|
||||
*/
|
||||
public static function index(array $props = []): string
|
||||
{
|
||||
return Uri::index($props)->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an URL is absolute
|
||||
*
|
||||
* @param string|null $url
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAbsolute(string $url = null): bool
|
||||
{
|
||||
// matches the following groups of URLs:
|
||||
// //example.com/uri
|
||||
// http://example.com/uri, https://example.com/uri, ftp://example.com/uri
|
||||
// mailto:example@example.com, geo:49.0158,8.3239?z=11
|
||||
return $url !== null && preg_match('!^(//|[a-z0-9+-.]+://|mailto:|tel:|geo:)!i', $url) === 1;
|
||||
}
|
||||
/**
|
||||
* Checks if an URL is absolute
|
||||
*
|
||||
* @param string|null $url
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAbsolute(string $url = null): bool
|
||||
{
|
||||
// matches the following groups of URLs:
|
||||
// //example.com/uri
|
||||
// http://example.com/uri, https://example.com/uri, ftp://example.com/uri
|
||||
// mailto:example@example.com, geo:49.0158,8.3239?z=11
|
||||
return $url !== null && preg_match('!^(//|[a-z0-9+-.]+://|mailto:|tel:|geo:)!i', $url) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a relative path into an absolute URL
|
||||
*
|
||||
* @param string|null $path
|
||||
* @param string|null $home
|
||||
* @return string
|
||||
*/
|
||||
public static function makeAbsolute(string $path = null, string $home = null): string
|
||||
{
|
||||
if ($path === '' || $path === '/' || $path === null) {
|
||||
return $home ?? static::home();
|
||||
}
|
||||
/**
|
||||
* Convert a relative path into an absolute URL
|
||||
*
|
||||
* @param string|null $path
|
||||
* @param string|null $home
|
||||
* @return string
|
||||
*/
|
||||
public static function makeAbsolute(string $path = null, string $home = null): string
|
||||
{
|
||||
if ($path === '' || $path === '/' || $path === null) {
|
||||
return $home ?? static::home();
|
||||
}
|
||||
|
||||
if (substr($path, 0, 1) === '#') {
|
||||
return $path;
|
||||
}
|
||||
if (substr($path, 0, 1) === '#') {
|
||||
return $path;
|
||||
}
|
||||
|
||||
if (static::isAbsolute($path)) {
|
||||
return $path;
|
||||
}
|
||||
if (static::isAbsolute($path)) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
// build the full url
|
||||
$path = ltrim($path, '/');
|
||||
$home ??= static::home();
|
||||
// build the full url
|
||||
$path = ltrim($path, '/');
|
||||
$home ??= static::home();
|
||||
|
||||
if (empty($path) === true) {
|
||||
return $home;
|
||||
}
|
||||
if (empty($path) === true) {
|
||||
return $home;
|
||||
}
|
||||
|
||||
return $home === '/' ? '/' . $path : $home . '/' . $path;
|
||||
}
|
||||
return $home === '/' ? '/' . $path : $home . '/' . $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path for the given url
|
||||
*
|
||||
* @param string|array|null $url
|
||||
* @param bool $leadingSlash
|
||||
* @param bool $trailingSlash
|
||||
* @return string
|
||||
*/
|
||||
public static function path($url = null, bool $leadingSlash = false, bool $trailingSlash = false): string
|
||||
{
|
||||
return Url::toObject($url)->path()->toString($leadingSlash, $trailingSlash);
|
||||
}
|
||||
/**
|
||||
* Returns the path for the given url
|
||||
*
|
||||
* @param string|array|null $url
|
||||
* @param bool $leadingSlash
|
||||
* @param bool $trailingSlash
|
||||
* @return string
|
||||
*/
|
||||
public static function path($url = null, bool $leadingSlash = false, bool $trailingSlash = false): string
|
||||
{
|
||||
return Url::toObject($url)->path()->toString($leadingSlash, $trailingSlash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query for the given url
|
||||
*
|
||||
* @param string|array|null $url
|
||||
* @return string
|
||||
*/
|
||||
public static function query($url = null): string
|
||||
{
|
||||
return Url::toObject($url)->query()->toString();
|
||||
}
|
||||
/**
|
||||
* Returns the query for the given url
|
||||
*
|
||||
* @param string|array|null $url
|
||||
* @return string
|
||||
*/
|
||||
public static function query($url = null): string
|
||||
{
|
||||
return Url::toObject($url)->query()->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last url the user has been on if detectable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function last(): string
|
||||
{
|
||||
return $_SERVER['HTTP_REFERER'] ?? '';
|
||||
}
|
||||
/**
|
||||
* Return the last url the user has been on if detectable
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function last(): string
|
||||
{
|
||||
return Environment::getGlobally('HTTP_REFERER', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortens the Url by removing all unnecessary parts
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $length
|
||||
* @param bool $base
|
||||
* @param string $rep
|
||||
* @return string
|
||||
*/
|
||||
public static function short($url = null, int $length = 0, bool $base = false, string $rep = '…'): string
|
||||
{
|
||||
$uri = static::toObject($url);
|
||||
/**
|
||||
* Shortens the Url by removing all unnecessary parts
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $length
|
||||
* @param bool $base
|
||||
* @param string $rep
|
||||
* @return string
|
||||
*/
|
||||
public static function short($url = null, int $length = 0, bool $base = false, string $rep = '…'): string
|
||||
{
|
||||
$uri = static::toObject($url);
|
||||
|
||||
$uri->fragment = null;
|
||||
$uri->query = null;
|
||||
$uri->password = null;
|
||||
$uri->port = null;
|
||||
$uri->scheme = null;
|
||||
$uri->username = null;
|
||||
$uri->fragment = null;
|
||||
$uri->query = null;
|
||||
$uri->password = null;
|
||||
$uri->port = null;
|
||||
$uri->scheme = null;
|
||||
$uri->username = null;
|
||||
|
||||
// remove the trailing slash from the path
|
||||
$uri->slash = false;
|
||||
// remove the trailing slash from the path
|
||||
$uri->slash = false;
|
||||
|
||||
$url = $base ? $uri->base() : $uri->toString();
|
||||
$url = str_replace('www.', '', $url);
|
||||
$url = $base ? $uri->base() : $uri->toString();
|
||||
$url = str_replace('www.', '', $url);
|
||||
|
||||
return Str::short($url, $length, $rep);
|
||||
}
|
||||
return Str::short($url, $length, $rep);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the path from the Url
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function stripPath($url = null): string
|
||||
{
|
||||
return static::toObject($url)->setPath(null)->toString();
|
||||
}
|
||||
/**
|
||||
* Removes the path from the Url
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function stripPath($url = null): string
|
||||
{
|
||||
return static::toObject($url)->setPath(null)->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the query string from the Url
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function stripQuery($url = null): string
|
||||
{
|
||||
return static::toObject($url)->setQuery(null)->toString();
|
||||
}
|
||||
/**
|
||||
* Removes the query string from the Url
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function stripQuery($url = null): string
|
||||
{
|
||||
return static::toObject($url)->setQuery(null)->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the fragment (hash) from the Url
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function stripFragment($url = null): string
|
||||
{
|
||||
return static::toObject($url)->setFragment(null)->toString();
|
||||
}
|
||||
/**
|
||||
* Removes the fragment (hash) from the Url
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function stripFragment($url = null): string
|
||||
{
|
||||
return static::toObject($url)->setFragment(null)->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart resolver for internal and external urls
|
||||
*
|
||||
* @param string $path
|
||||
* @param mixed $options
|
||||
* @return string
|
||||
*/
|
||||
public static function to(string $path = null, $options = null): string
|
||||
{
|
||||
// make sure $path is string
|
||||
$path ??= '';
|
||||
/**
|
||||
* Smart resolver for internal and external urls
|
||||
*
|
||||
* @param string $path
|
||||
* @param mixed $options
|
||||
* @return string
|
||||
*/
|
||||
public static function to(string $path = null, $options = null): string
|
||||
{
|
||||
// make sure $path is string
|
||||
$path ??= '';
|
||||
|
||||
// keep relative urls
|
||||
if (substr($path, 0, 2) === './' || substr($path, 0, 3) === '../') {
|
||||
return $path;
|
||||
}
|
||||
// keep relative urls
|
||||
if (substr($path, 0, 2) === './' || substr($path, 0, 3) === '../') {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$url = static::makeAbsolute($path);
|
||||
$url = static::makeAbsolute($path);
|
||||
|
||||
if ($options === null) {
|
||||
return $url;
|
||||
}
|
||||
if ($options === null) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
return (new Uri($url, $options))->toString();
|
||||
}
|
||||
return (new Uri($url, $options))->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the Url to a Uri object
|
||||
*
|
||||
* @param string $url
|
||||
* @return \Kirby\Http\Uri
|
||||
*/
|
||||
public static function toObject($url = null)
|
||||
{
|
||||
return $url === null ? Uri::current() : new Uri($url);
|
||||
}
|
||||
/**
|
||||
* Converts the Url to a Uri object
|
||||
*
|
||||
* @param string $url
|
||||
* @return \Kirby\Http\Uri
|
||||
*/
|
||||
public static function toObject($url = null)
|
||||
{
|
||||
return $url === null ? Uri::current() : new Uri($url);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,233 +20,233 @@ use Kirby\Toolkit\Str;
|
|||
*/
|
||||
class Visitor
|
||||
{
|
||||
/**
|
||||
* IP address
|
||||
* @var string|null
|
||||
*/
|
||||
protected $ip;
|
||||
/**
|
||||
* IP address
|
||||
* @var string|null
|
||||
*/
|
||||
protected $ip;
|
||||
|
||||
/**
|
||||
* user agent
|
||||
* @var string|null
|
||||
*/
|
||||
protected $userAgent;
|
||||
/**
|
||||
* user agent
|
||||
* @var string|null
|
||||
*/
|
||||
protected $userAgent;
|
||||
|
||||
/**
|
||||
* accepted language
|
||||
* @var string|null
|
||||
*/
|
||||
protected $acceptedLanguage;
|
||||
/**
|
||||
* accepted language
|
||||
* @var string|null
|
||||
*/
|
||||
protected $acceptedLanguage;
|
||||
|
||||
/**
|
||||
* accepted mime type
|
||||
* @var string|null
|
||||
*/
|
||||
protected $acceptedMimeType;
|
||||
/**
|
||||
* accepted mime type
|
||||
* @var string|null
|
||||
*/
|
||||
protected $acceptedMimeType;
|
||||
|
||||
/**
|
||||
* Creates a new visitor object.
|
||||
* Optional arguments can be passed to
|
||||
* modify the information about the visitor.
|
||||
*
|
||||
* By default everything is pulled from $_SERVER
|
||||
*
|
||||
* @param array $arguments
|
||||
*/
|
||||
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'] ?? '');
|
||||
}
|
||||
/**
|
||||
* Creates a new visitor object.
|
||||
* Optional arguments can be passed to
|
||||
* modify the information about the visitor.
|
||||
*
|
||||
* By default everything is pulled from $_SERVER
|
||||
*
|
||||
* @param array $arguments
|
||||
*/
|
||||
public function __construct(array $arguments = [])
|
||||
{
|
||||
$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', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the accepted language if
|
||||
* provided or returns the user's
|
||||
* accepted language otherwise
|
||||
*
|
||||
* @param string|null $acceptedLanguage
|
||||
* @return \Kirby\Toolkit\Obj|\Kirby\Http\Visitor|null
|
||||
*/
|
||||
public function acceptedLanguage(string $acceptedLanguage = null)
|
||||
{
|
||||
if ($acceptedLanguage === null) {
|
||||
return $this->acceptedLanguages()->first();
|
||||
}
|
||||
/**
|
||||
* Sets the accepted language if
|
||||
* provided or returns the user's
|
||||
* accepted language otherwise
|
||||
*
|
||||
* @param string|null $acceptedLanguage
|
||||
* @return \Kirby\Toolkit\Obj|\Kirby\Http\Visitor|null
|
||||
*/
|
||||
public function acceptedLanguage(string $acceptedLanguage = null)
|
||||
{
|
||||
if ($acceptedLanguage === null) {
|
||||
return $this->acceptedLanguages()->first();
|
||||
}
|
||||
|
||||
$this->acceptedLanguage = $acceptedLanguage;
|
||||
return $this;
|
||||
}
|
||||
$this->acceptedLanguage = $acceptedLanguage;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all accepted languages
|
||||
* including their quality and locale
|
||||
*
|
||||
* @return \Kirby\Toolkit\Collection
|
||||
*/
|
||||
public function acceptedLanguages()
|
||||
{
|
||||
$accepted = Str::accepted($this->acceptedLanguage);
|
||||
$languages = [];
|
||||
/**
|
||||
* Returns an array of all accepted languages
|
||||
* including their quality and locale
|
||||
*
|
||||
* @return \Kirby\Toolkit\Collection
|
||||
*/
|
||||
public function acceptedLanguages()
|
||||
{
|
||||
$accepted = Str::accepted($this->acceptedLanguage);
|
||||
$languages = [];
|
||||
|
||||
foreach ($accepted as $language) {
|
||||
$value = $language['value'];
|
||||
$parts = Str::split($value, '-');
|
||||
$code = isset($parts[0]) ? Str::lower($parts[0]) : null;
|
||||
$region = isset($parts[1]) ? Str::upper($parts[1]) : null;
|
||||
$locale = $region ? $code . '_' . $region : $code;
|
||||
foreach ($accepted as $language) {
|
||||
$value = $language['value'];
|
||||
$parts = Str::split($value, '-');
|
||||
$code = isset($parts[0]) ? Str::lower($parts[0]) : null;
|
||||
$region = isset($parts[1]) ? Str::upper($parts[1]) : null;
|
||||
$locale = $region ? $code . '_' . $region : $code;
|
||||
|
||||
$languages[$locale] = new Obj([
|
||||
'code' => $code,
|
||||
'locale' => $locale,
|
||||
'original' => $value,
|
||||
'quality' => $language['quality'],
|
||||
'region' => $region,
|
||||
]);
|
||||
}
|
||||
$languages[$locale] = new Obj([
|
||||
'code' => $code,
|
||||
'locale' => $locale,
|
||||
'original' => $value,
|
||||
'quality' => $language['quality'],
|
||||
'region' => $region,
|
||||
]);
|
||||
}
|
||||
|
||||
return new Collection($languages);
|
||||
}
|
||||
return new Collection($languages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user accepts the given language
|
||||
*
|
||||
* @param string $code
|
||||
* @return bool
|
||||
*/
|
||||
public function acceptsLanguage(string $code): bool
|
||||
{
|
||||
$mode = Str::contains($code, '_') === true ? 'locale' : 'code';
|
||||
/**
|
||||
* Checks if the user accepts the given language
|
||||
*
|
||||
* @param string $code
|
||||
* @return bool
|
||||
*/
|
||||
public function acceptsLanguage(string $code): bool
|
||||
{
|
||||
$mode = Str::contains($code, '_') === true ? 'locale' : 'code';
|
||||
|
||||
foreach ($this->acceptedLanguages() as $language) {
|
||||
if ($language->$mode() === $code) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
foreach ($this->acceptedLanguages() as $language) {
|
||||
if ($language->$mode() === $code) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the accepted mime type if
|
||||
* provided or returns the user's
|
||||
* accepted mime type otherwise
|
||||
*
|
||||
* @param string|null $acceptedMimeType
|
||||
* @return \Kirby\Toolkit\Obj|\Kirby\Http\Visitor
|
||||
*/
|
||||
public function acceptedMimeType(string $acceptedMimeType = null)
|
||||
{
|
||||
if ($acceptedMimeType === null) {
|
||||
return $this->acceptedMimeTypes()->first();
|
||||
}
|
||||
/**
|
||||
* Sets the accepted mime type if
|
||||
* provided or returns the user's
|
||||
* accepted mime type otherwise
|
||||
*
|
||||
* @param string|null $acceptedMimeType
|
||||
* @return \Kirby\Toolkit\Obj|\Kirby\Http\Visitor
|
||||
*/
|
||||
public function acceptedMimeType(string $acceptedMimeType = null)
|
||||
{
|
||||
if ($acceptedMimeType === null) {
|
||||
return $this->acceptedMimeTypes()->first();
|
||||
}
|
||||
|
||||
$this->acceptedMimeType = $acceptedMimeType;
|
||||
return $this;
|
||||
}
|
||||
$this->acceptedMimeType = $acceptedMimeType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of all accepted mime types
|
||||
*
|
||||
* @return \Kirby\Toolkit\Collection
|
||||
*/
|
||||
public function acceptedMimeTypes()
|
||||
{
|
||||
$accepted = Str::accepted($this->acceptedMimeType);
|
||||
$mimes = [];
|
||||
/**
|
||||
* Returns a collection of all accepted mime types
|
||||
*
|
||||
* @return \Kirby\Toolkit\Collection
|
||||
*/
|
||||
public function acceptedMimeTypes()
|
||||
{
|
||||
$accepted = Str::accepted($this->acceptedMimeType);
|
||||
$mimes = [];
|
||||
|
||||
foreach ($accepted as $mime) {
|
||||
$mimes[$mime['value']] = new Obj([
|
||||
'type' => $mime['value'],
|
||||
'quality' => $mime['quality'],
|
||||
]);
|
||||
}
|
||||
foreach ($accepted as $mime) {
|
||||
$mimes[$mime['value']] = new Obj([
|
||||
'type' => $mime['value'],
|
||||
'quality' => $mime['quality'],
|
||||
]);
|
||||
}
|
||||
|
||||
return new Collection($mimes);
|
||||
}
|
||||
return new Collection($mimes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user accepts the given mime type
|
||||
*
|
||||
* @param string $mimeType
|
||||
* @return bool
|
||||
*/
|
||||
public function acceptsMimeType(string $mimeType): bool
|
||||
{
|
||||
return Mime::isAccepted($mimeType, $this->acceptedMimeType);
|
||||
}
|
||||
/**
|
||||
* Checks if the user accepts the given mime type
|
||||
*
|
||||
* @param string $mimeType
|
||||
* @return bool
|
||||
*/
|
||||
public function acceptsMimeType(string $mimeType): bool
|
||||
{
|
||||
return Mime::isAccepted($mimeType, $this->acceptedMimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MIME type from the provided list that
|
||||
* is most accepted (= preferred) by the visitor
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param string ...$mimeTypes MIME types to query for
|
||||
* @return string|null Preferred MIME type
|
||||
*/
|
||||
public function preferredMimeType(string ...$mimeTypes): ?string
|
||||
{
|
||||
foreach ($this->acceptedMimeTypes() as $acceptedMime) {
|
||||
// look for direct matches
|
||||
if (in_array($acceptedMime->type(), $mimeTypes)) {
|
||||
return $acceptedMime->type();
|
||||
}
|
||||
/**
|
||||
* Returns the MIME type from the provided list that
|
||||
* is most accepted (= preferred) by the visitor
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @param string ...$mimeTypes MIME types to query for
|
||||
* @return string|null Preferred MIME type
|
||||
*/
|
||||
public function preferredMimeType(string ...$mimeTypes): ?string
|
||||
{
|
||||
foreach ($this->acceptedMimeTypes() as $acceptedMime) {
|
||||
// look for direct matches
|
||||
if (in_array($acceptedMime->type(), $mimeTypes)) {
|
||||
return $acceptedMime->type();
|
||||
}
|
||||
|
||||
// test each option against wildcard `Accept` values
|
||||
foreach ($mimeTypes as $expectedMime) {
|
||||
if (Mime::matches($expectedMime, $acceptedMime->type()) === true) {
|
||||
return $expectedMime;
|
||||
}
|
||||
}
|
||||
}
|
||||
// test each option against wildcard `Accept` values
|
||||
foreach ($mimeTypes as $expectedMime) {
|
||||
if (Mime::matches($expectedMime, $acceptedMime->type()) === true) {
|
||||
return $expectedMime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the visitor prefers a JSON response over
|
||||
* an HTML response based on the `Accept` request header
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function prefersJson(): bool
|
||||
{
|
||||
return $this->preferredMimeType('application/json', 'text/html') === 'application/json';
|
||||
}
|
||||
/**
|
||||
* Returns true if the visitor prefers a JSON response over
|
||||
* an HTML response based on the `Accept` request header
|
||||
* @since 3.3.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function prefersJson(): bool
|
||||
{
|
||||
return $this->preferredMimeType('application/json', 'text/html') === 'application/json';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ip address if provided
|
||||
* or returns the ip of the current
|
||||
* visitor otherwise
|
||||
*
|
||||
* @param string|null $ip
|
||||
* @return string|Visitor|null
|
||||
*/
|
||||
public function ip(string $ip = null)
|
||||
{
|
||||
if ($ip === null) {
|
||||
return $this->ip;
|
||||
}
|
||||
$this->ip = $ip;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the ip address if provided
|
||||
* or returns the ip of the current
|
||||
* visitor otherwise
|
||||
*
|
||||
* @param string|null $ip
|
||||
* @return string|Visitor|null
|
||||
*/
|
||||
public function ip(string $ip = null)
|
||||
{
|
||||
if ($ip === null) {
|
||||
return $this->ip;
|
||||
}
|
||||
$this->ip = $ip;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user agent if provided
|
||||
* or returns the user agent string of
|
||||
* the current visitor otherwise
|
||||
*
|
||||
* @param string|null $userAgent
|
||||
* @return string|Visitor|null
|
||||
*/
|
||||
public function userAgent(string $userAgent = null)
|
||||
{
|
||||
if ($userAgent === null) {
|
||||
return $this->userAgent;
|
||||
}
|
||||
$this->userAgent = $userAgent;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Sets the user agent if provided
|
||||
* or returns the user agent string of
|
||||
* the current visitor otherwise
|
||||
*
|
||||
* @param string|null $userAgent
|
||||
* @return string|Visitor|null
|
||||
*/
|
||||
public function userAgent(string $userAgent = null)
|
||||
{
|
||||
if ($userAgent === null) {
|
||||
return $this->userAgent;
|
||||
}
|
||||
$this->userAgent = $userAgent;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue