Upgrade to 3.7.0

This commit is contained in:
Bastian Allgeier
2022-06-27 10:02:22 +02:00
parent 0751a6510d
commit 1c22148d7b
674 changed files with 5052 additions and 3082 deletions

0
kirby/src/Toolkit/A.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Collection.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Component.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Config.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Controller.php Normal file → Executable file
View File

26
kirby/src/Toolkit/Date.php Normal file → Executable file
View File

@@ -359,6 +359,32 @@ class Date extends DateTime
return $this;
}
/**
* Rounds the minutes of the given date
* by the defined step
* @since 3.7.0
*
* @param string|null $date
* @param int|array|null $step array of `unit` and `size` to round to nearest
* @return int|null
*/
public static function roundedTimestamp(?string $date = null, $step = null): ?int
{
if ($date = static::optional($date)) {
if ($step !== null) {
$step = static::stepConfig($step, [
'unit' => 'minute',
'size' => 1
]);
$date->round($step['unit'], $step['size']);
}
return $date->timestamp();
}
return null;
}
/**
* Gets or sets the second value
*

0
kirby/src/Toolkit/Dom.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Escape.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Facade.php Normal file → Executable file
View File

12
kirby/src/Toolkit/Html.php Normal file → Executable file
View File

@@ -133,9 +133,11 @@ class Html extends Xml
* Key-value array: A list of attributes will be generated. Don't pass a second argument in that case.
* @param mixed $value If used with a `$name` string, pass the value of the attribute here.
* If used with a `$name` array, this can be set to `false` to disable attribute sorting.
* @param string|null $before An optional string that will be prepended if the result is not empty
* @param string|null $after An optional string that will be appended if the result is not empty
* @return string|null The generated HTML attributes string
*/
public static function attr($name, $value = null): ?string
public static function attr($name, $value = null, ?string $before = null, ?string $after = null): ?string
{
// HTML supports boolean attributes without values
if (is_array($name) === false && is_bool($value) === true) {
@@ -153,7 +155,13 @@ class Html extends Xml
$entities = parent::entities();
$html = array_keys($entities);
$xml = array_values($entities);
return str_replace($xml, $html, $attr);
$attr = str_replace($xml, $html, $attr);
if ($attr) {
return $before . $attr . $after;
}
return null;
}
/**

16
kirby/src/Toolkit/I18n.php Normal file → Executable file
View File

@@ -52,22 +52,6 @@ class I18n
*/
protected static $decimalsFormatters = [];
/**
* Returns the first fallback locale
*
* @deprecated 3.5.1 Use `\Kirby\Toolkit\I18n::fallbacks()` instead
* @todo Remove in 3.7.0
*
* @return string
*/
public static function fallback(): string
{
// @codeCoverageIgnoreStart
deprecated('I18n::fallback() has been deprecated. Use I18n::fallbacks() instead.');
return static::fallbacks()[0];
// @codeCoverageIgnoreEnd
}
/**
* Returns the list of fallback locales
*

0
kirby/src/Toolkit/Iterator.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Locale.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Obj.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Pagination.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Properties.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Query.php Normal file → Executable file
View File

0
kirby/src/Toolkit/Silo.php Normal file → Executable file
View File

202
kirby/src/Toolkit/Str.php Normal file → Executable file
View File

@@ -5,6 +5,7 @@ namespace Kirby\Toolkit;
use DateTime;
use Exception;
use IntlDateFormatter;
use Kirby\Cms\Helpers;
use Kirby\Exception\InvalidArgumentException;
/**
@@ -170,7 +171,7 @@ class Str
}
/**
* Returns the rest of the string after the given character
* Returns the rest of the string after the given substring or character
*
* @param string $string
* @param string $needle
@@ -188,6 +189,28 @@ class Str
return static::substr($string, $position + static::length($needle));
}
/**
* Removes the given substring or character only from the start of the string
* @since 3.7.0
*
* @param string $string
* @param string $needle
* @param bool $caseInsensitive
* @return string
*/
public static function afterStart(string $string, string $needle, bool $caseInsensitive = false): string
{
if ($needle === '') {
return $string;
}
if (static::startsWith($string, $needle, $caseInsensitive) === true) {
return static::substr($string, static::length($needle));
}
return $string;
}
/**
* Convert a string to 7-bit ASCII.
*
@@ -212,7 +235,7 @@ class Str
}
/**
* Returns the beginning of a string before the given character
* Returns the beginning of a string before the given substring or character
*
* @param string $string
* @param string $needle
@@ -230,6 +253,28 @@ class Str
return static::substr($string, 0, $position);
}
/**
* Removes the given substring or character only from the end of the string
* @since 3.7.0
*
* @param string $string
* @param string $needle
* @param bool $caseInsensitive
* @return string
*/
public static function beforeEnd(string $string, string $needle, bool $caseInsensitive = false): string
{
if ($needle === '') {
return $string;
}
if (static::endsWith($string, $needle, $caseInsensitive) === true) {
return static::substr($string, 0, -static::length($needle));
}
return $string;
}
/**
* Returns everything between two strings from the first occurrence of a given string
*
@@ -243,6 +288,17 @@ class Str
return static::before(static::after($string, $start), $end);
}
/**
* Converts a string to camel case
*
* @param string $value The string to convert
* @return string
*/
public static function camel(string $value = null): string
{
return lcfirst(static::studly($value));
}
/**
* Checks if a str contains another string
*
@@ -366,7 +422,7 @@ class Str
* @param bool $caseInsensitive
* @return bool
*/
public static function endsWith(string $string, string $needle, bool $caseInsensitive = false): bool
public static function endsWith(string $string = null, string $needle, bool $caseInsensitive = false): bool
{
if ($needle === '') {
return true;
@@ -382,6 +438,23 @@ class Str
return $needle === $probe;
}
/**
* Escape string for context specific output
* @since 3.7.0
*
* @param string $string Untrusted data
* @param string $context Location of output (`html`, `attr`, `js`, `css`, `url` or `xml`)
* @return string Escaped data
*/
public static function esc(string $string, string $context = 'html'): string
{
if (method_exists('Kirby\Toolkit\Escape', $context) === true) {
return Escape::$context($string);
}
return $string;
}
/**
* Creates an excerpt of a string
* It removes all html tags first and then cuts the string
@@ -457,18 +530,40 @@ class Str
return static::substr($string, $position);
}
/**
* Adds `-1` to a string or increments the ending number to allow `-2`, `-3`, etc.
* @since 3.7.0
*
* @param string $string The string to increment
* @param string $separator
* @param int $first Starting number
* @return string
*/
public static function increment(string $string, string $separator = '-', int $first = 1): string
{
preg_match('/(.+)' . preg_quote($separator, '/') . '([0-9]+)$/', $string, $matches);
if (isset($matches[2]) === true) {
// increment the existing ending number
return $matches[1] . $separator . ((int)$matches[2] + 1);
}
// append a new ending number
return $string . $separator . $first;
}
/**
* Checks if the given string is a URL
*
* @param string|null $string
* @return bool
* @deprecated 3.6.0 use `Kirby\Toolkit\V::url()` instead
* @todo Throw deprecation warning in 3.7.0
* @todo Remove in 3.8.0
* @codeCoverageIgnore
*/
public static function isURL(?string $string = null): bool
{
Helpers::deprecated('Toolkit\Str::isUrl() has been deprecated and will be removed in Kirby 3.8.0. Use Toolkit\V::url() instead.');
return filter_var($string, FILTER_VALIDATE_URL) !== false;
}
@@ -1050,6 +1145,18 @@ class Str
return static::position($string, $needle, $caseInsensitive) === 0;
}
/**
* Converts a string to studly caps case
* @since 3.7.0
*
* @param string $value The string to convert
* @return string
*/
public static function studly(string $value = null): string
{
return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $value)));
}
/**
* A UTF-8 safe version of substr()
*
@@ -1077,36 +1184,19 @@ class Str
* @param array $data Associative array with placeholders as
* keys and replacements as values.
* Supports query syntax.
* @param string|array|null $fallback An options array that contains:
* - fallback: if a token does not have any matches
* - callback: to be able to handle each matching result
* - start: start placeholder
* - end: end placeholder
* A simple fallback string is supported for compatibility (but deprecated).
* @param string $start Placeholder start characters (deprecated)
* @param string $end Placeholder end characters (deprecated)
*
* @todo Remove `$start` and `$end` parameters, rename `$fallback` to `$options` and only support `array` type for `$options` in 3.7.0
*
* @param array $options An options array that contains:
* - fallback: if a token does not have any matches
* - callback: to be able to handle each matching result
* - start: start placeholder
* - end: end placeholder
* @return string The filled-in string
*/
public static function template(string $string = null, array $data = [], $fallback = null, string $start = '{{', string $end = '}}'): string
public static function template(string $string = null, array $data = [], array $options = []): string
{
// @codeCoverageIgnoreStart
if (
is_string($fallback) === true ||
$start !== '{{' ||
$end !== '}}'
) {
deprecated('Str::template(): The $fallback, $start and $end parameters have been deprecated. Please pass an array to the $options parameter instead with `fallback`, `start` or `end` keys: Str::template($string, $data, $options)');
}
// @codeCoverageIgnoreEnd
$options = $fallback;
$fallback = is_string($options) === true ? $options : ($options['fallback'] ?? null);
$fallback = $options['fallback'] ?? null;
$callback = is_a(($options['callback'] ?? null), 'Closure') === true ? $options['callback'] : null;
$start = (string)($options['start'] ?? $start);
$end = (string)($options['end'] ?? $end);
$start = (string)($options['start'] ?? '{{');
$end = (string)($options['end'] ?? '}}');
// make sure $string is string
$string ??= '';
@@ -1144,15 +1234,11 @@ class Str
* Converts a filesize string with shortcuts
* like M, G or K to an integer value
*
* @param mixed $size
* @param string $size
* @return int
*/
public static function toBytes($size): int
public static function toBytes(string $size): int
{
// TODO: remove in 3.7.0
// in favor of strict parameter type hint
$size ??= '';
$size = trim($size);
$last = strtolower($size[strlen($size)-1] ?? '');
$size = (int)$size;
@@ -1283,6 +1369,36 @@ class Str
return mb_strtoupper($string ?? '', 'UTF-8');
}
/**
* Creates a compliant v4 UUID
* Taken from: https://github.com/symfony/polyfill
* @since 3.7.0
*
* @return string
*/
public static function uuid(): string
{
$uuid = bin2hex(random_bytes(16));
return sprintf(
'%08s-%04s-4%03s-%04x-%012s',
// 32 bits for "time_low"
substr($uuid, 0, 8),
// 16 bits for "time_mid"
substr($uuid, 8, 4),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 4
substr($uuid, 13, 3),
// 16 bits:
// * 8 bits for "clk_seq_hi_res",
// * 8 bits for "clk_seq_low",
// two most significant bits holds zero and one for variant DCE1.1
hexdec(substr($uuid, 16, 4)) & 0x3fff | 0x8000,
// 48 bits for "node"
substr($uuid, 20, 12)
);
}
/**
* The widont function makes sure that there are no
* typographical widows at the end of a paragraph
@@ -1309,4 +1425,18 @@ class Str
return ' ' . $matches[2];
}, $string);
}
/**
* Wraps the string with the given string(s)
* @since 3.7.0
*
* @param string $string String to wrap
* @param string $before String to prepend
* @param string|null $after String to append (if different from `$before`)
* @return string
*/
public static function wrap(string $string, string $before, string $after = null): string
{
return $before . $string . ($after ?? $before);
}
}

0
kirby/src/Toolkit/Tpl.php Normal file → Executable file
View File

119
kirby/src/Toolkit/V.php Normal file → Executable file
View File

@@ -43,6 +43,75 @@ class V
return $errors === true ? [] : $errors;
}
/**
* Runs a number of validators on a set of data and
* checks if the data is invalid
* @since 3.7.0
*
* @param array $data
* @param array $rules
* @param array $messages
* @return array
*/
public static function invalid(array $data = [], array $rules = [], array $messages = []): array
{
$errors = [];
foreach ($rules as $field => $validations) {
$validationIndex = -1;
// See: http://php.net/manual/en/types.comparisons.php
// only false for: null, undefined variable, '', []
$value = $data[$field] ?? null;
$filled = $value !== null && $value !== '' && $value !== [];
$message = $messages[$field] ?? $field;
// True if there is an error message for each validation method.
$messageArray = is_array($message);
foreach ($validations as $method => $options) {
// If the index is numeric, there is no option
// and `$value` is sent directly as a `$options` parameter
if (is_numeric($method) === true) {
$method = $options;
$options = [$value];
} else {
if (is_array($options) === false) {
$options = [$options];
}
array_unshift($options, $value);
}
$validationIndex++;
if ($method === 'required') {
if ($filled) {
// Field is required and filled.
continue;
}
} elseif ($filled) {
if (V::$method(...$options) === true) {
// Field is filled and passes validation method.
continue;
}
} else {
// If a field is not required and not filled, no validation should be done.
continue;
}
// If no continue was called we have a failed validation.
if ($messageArray) {
$errors[$field][] = $message[$validationIndex] ?? $field;
} else {
$errors[$field] = $message;
}
}
}
return $errors;
}
/**
* Creates a useful error message for the given validator
* and the arguments. This is used mainly internally
@@ -321,6 +390,23 @@ V::$validators = [
return true;
},
/**
* Checks for empty values
*/
'empty' => function ($value = null): bool {
$empty = ['', null, []];
if (in_array($value, $empty, true) === true) {
return true;
}
if (is_countable($value) === true) {
return count($value) === 0;
}
return false;
},
/**
* Checks if the given string ends with the given value
*/
@@ -360,6 +446,19 @@ V::$validators = [
return filter_var($value, FILTER_VALIDATE_IP) !== false;
},
/**
* Checks for valid json
*/
'json' => function ($value): bool {
if (!is_string($value) || $value === '') {
return false;
}
json_decode($value);
return json_last_error() === JSON_ERROR_NONE;
},
/**
* Checks if the value is lower than the second value
*/
@@ -430,6 +529,13 @@ V::$validators = [
return V::contains($value, $needle) === false;
},
/**
* Checks that the given value is not empty
*/
'notEmpty' => function ($value): bool {
return V::empty($value) === false;
},
/**
* Checks that the given value is not in the given list of values
*/
@@ -445,11 +551,16 @@ V::$validators = [
},
/**
* Checks if the value is present in the given array
* Checks if the value is present
*/
'required' => function ($key, array $array): bool {
return isset($array[$key]) === true &&
V::notIn($array[$key], [null, '', []]) === true;
'required' => function ($value, $array = null): bool {
// with reference array
if (is_array($array) === true) {
return isset($array[$value]) === true && V::notEmpty($array[$value]) === true;
}
// without reference array
return V::notEmpty($value);
},
/**

0
kirby/src/Toolkit/View.php Normal file → Executable file
View File

3
kirby/src/Toolkit/Xml.php Normal file → Executable file
View File

@@ -421,8 +421,7 @@ class Xml
return $value;
}
// TODO: in 3.7.0 use ENT_NOQUOTES | ENT_XML1 instead
$encoded = htmlentities($value, ENT_COMPAT);
$encoded = htmlentities($value, ENT_NOQUOTES | ENT_XML1);
if ($encoded === $value) {
// no CDATA block needed
return $value;