Upgrade to 3.8.0
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Kirby\Toolkit;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
@@ -21,14 +22,10 @@ class A
|
||||
{
|
||||
/**
|
||||
* Appends the given array
|
||||
*
|
||||
* @param array $array
|
||||
* @param array $append
|
||||
* @return array
|
||||
*/
|
||||
public static function append(array $array, array $append): array
|
||||
{
|
||||
return $array + $append;
|
||||
return static::merge($array, $append, A::MERGE_APPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,14 +34,12 @@ class A
|
||||
* applying the passed parameters
|
||||
* @since 3.5.6
|
||||
*
|
||||
* @param array $array
|
||||
* @param mixed ...$args Parameters to pass to the closures
|
||||
* @return array
|
||||
*/
|
||||
public static function apply(array $array, ...$args): array
|
||||
{
|
||||
array_walk_recursive($array, function (&$item) use ($args) {
|
||||
if (is_a($item, 'Closure')) {
|
||||
if ($item instanceof Closure) {
|
||||
$item = $item(...$args);
|
||||
}
|
||||
});
|
||||
@@ -73,13 +68,16 @@ class A
|
||||
* </code>
|
||||
*
|
||||
* @param array $array The source array
|
||||
* @param mixed $key The key to look for
|
||||
* @param mixed $default Optional default value, which should be
|
||||
* returned if no element has been found
|
||||
* @return mixed
|
||||
* @param string|int|array|null $key The key to look for
|
||||
* @param mixed $default Optional default value, which
|
||||
* should be returned if no element
|
||||
* has been found
|
||||
*/
|
||||
public static function get($array, $key, $default = null)
|
||||
{
|
||||
public static function get(
|
||||
$array,
|
||||
string|int|array|null $key,
|
||||
$default = null
|
||||
) {
|
||||
if (is_array($array) === false) {
|
||||
return $array;
|
||||
}
|
||||
@@ -141,11 +139,9 @@ class A
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param mixed $separator
|
||||
* @return string
|
||||
* Joins the elements of an array to a string
|
||||
*/
|
||||
public static function join($value, $separator = ', ')
|
||||
public static function join(array|string $value, string $separator = ', '): string
|
||||
{
|
||||
if (is_string($value) === true) {
|
||||
return $value;
|
||||
@@ -160,19 +156,19 @@ class A
|
||||
/**
|
||||
* Merges arrays recursively
|
||||
*
|
||||
* @param array $array1
|
||||
* @param array $array2
|
||||
* @param int $mode Behavior for elements with numeric keys;
|
||||
* A::MERGE_APPEND: elements are appended, keys are reset;
|
||||
* A::MERGE_OVERWRITE: elements are overwritten, keys are preserved
|
||||
* A::MERGE_REPLACE: non-associative arrays are completely replaced
|
||||
* @return array
|
||||
*/
|
||||
public static function merge($array1, $array2, int $mode = A::MERGE_APPEND)
|
||||
public static function merge(array $array1, array $array2, int $mode = A::MERGE_APPEND): array
|
||||
{
|
||||
$merged = $array1;
|
||||
|
||||
if (static::isAssociative($array1) === false && $mode === static::MERGE_REPLACE) {
|
||||
if (
|
||||
static::isAssociative($array1) === false &&
|
||||
$mode === static::MERGE_REPLACE
|
||||
) {
|
||||
return $array2;
|
||||
}
|
||||
|
||||
@@ -182,7 +178,11 @@ class A
|
||||
$merged[] = $value;
|
||||
|
||||
// recursively merge the two array values
|
||||
} elseif (is_array($value) === true && isset($merged[$key]) === true && is_array($merged[$key]) === true) {
|
||||
} elseif (
|
||||
is_array($value) === true &&
|
||||
isset($merged[$key]) === true &&
|
||||
is_array($merged[$key]) === true
|
||||
) {
|
||||
$merged[$key] = static::merge($merged[$key], $value, $mode);
|
||||
|
||||
// simply overwrite with the value from the second array
|
||||
@@ -230,7 +230,7 @@ class A
|
||||
* @return array The result array with all values
|
||||
* from that column.
|
||||
*/
|
||||
public static function pluck(array $array, string $key)
|
||||
public static function pluck(array $array, string $key): array
|
||||
{
|
||||
$output = [];
|
||||
foreach ($array as $a) {
|
||||
@@ -244,10 +244,6 @@ class A
|
||||
|
||||
/**
|
||||
* Prepends the given array
|
||||
*
|
||||
* @param array $array
|
||||
* @param array $prepend
|
||||
* @return array
|
||||
*/
|
||||
public static function prepend(array $array, array $prepend): array
|
||||
{
|
||||
@@ -337,11 +333,6 @@ class A
|
||||
/**
|
||||
* Returns a number of random elements from an array,
|
||||
* either in original or shuffled order
|
||||
*
|
||||
* @param array $array
|
||||
* @param int $count
|
||||
* @param bool $shuffle
|
||||
* @return array
|
||||
*/
|
||||
public static function random(array $array, int $count = 1, bool $shuffle = false): array
|
||||
{
|
||||
@@ -400,10 +391,6 @@ class A
|
||||
* A simple wrapper around array_map
|
||||
* with a sane argument order
|
||||
* @since 3.6.0
|
||||
*
|
||||
* @param array $array
|
||||
* @param callable $map
|
||||
* @return array
|
||||
*/
|
||||
public static function map(array $array, callable $map): array
|
||||
{
|
||||
@@ -412,11 +399,6 @@ class A
|
||||
|
||||
/**
|
||||
* Move an array item to a new index
|
||||
*
|
||||
* @param array $array
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @return array
|
||||
*/
|
||||
public static function move(array $array, int $from, int $to): array
|
||||
{
|
||||
@@ -480,10 +462,8 @@ class A
|
||||
* Normalizes an array into a nested form by converting
|
||||
* dot notation in keys to nested structures
|
||||
*
|
||||
* @param array $array
|
||||
* @param array $ignore List of keys in dot notation that should
|
||||
* not be converted to a nested structure
|
||||
* @return array
|
||||
*/
|
||||
public static function nest(array $array, array $ignore = []): array
|
||||
{
|
||||
@@ -661,8 +641,12 @@ class A
|
||||
* @param int $decimals The number of decimals to return
|
||||
* @return float The average value
|
||||
*/
|
||||
public static function average(array $array, int $decimals = 0): float
|
||||
public static function average(array $array, int $decimals = 0): float|null
|
||||
{
|
||||
if (empty($array) === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return round((array_sum($array) / sizeof($array)), $decimals);
|
||||
}
|
||||
|
||||
@@ -681,11 +665,8 @@ class A
|
||||
* // 'password' => 'super-secret'
|
||||
* // ];
|
||||
* </code>
|
||||
*
|
||||
* @param array ...$arrays
|
||||
* @return array
|
||||
*/
|
||||
public static function extend(...$arrays): array
|
||||
public static function extend(array ...$arrays): array
|
||||
{
|
||||
return array_merge_recursive(...$arrays);
|
||||
}
|
||||
@@ -713,15 +694,11 @@ class A
|
||||
* }
|
||||
* ]);
|
||||
* </code>
|
||||
*
|
||||
* @param array $array
|
||||
* @param array $update
|
||||
* @return array
|
||||
*/
|
||||
public static function update(array $array, array $update): array
|
||||
{
|
||||
foreach ($update as $key => $value) {
|
||||
if (is_a($value, 'Closure') === true) {
|
||||
if ($value instanceof Closure) {
|
||||
$array[$key] = call_user_func($value, static::get($array, $key));
|
||||
} else {
|
||||
$array[$key] = $value;
|
||||
@@ -734,29 +711,24 @@ class A
|
||||
/**
|
||||
* Wraps the given value in an array
|
||||
* if it's not an array yet.
|
||||
*
|
||||
* @param mixed|null $array
|
||||
* @return array
|
||||
*/
|
||||
public static function wrap($array = null): array
|
||||
{
|
||||
if ($array === null) {
|
||||
return [];
|
||||
} elseif (is_array($array) === false) {
|
||||
return [$array];
|
||||
} else {
|
||||
return $array;
|
||||
}
|
||||
|
||||
if (is_array($array) === false) {
|
||||
return [$array];
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the array using the given callback
|
||||
* using both value and key
|
||||
* @since 3.6.5
|
||||
*
|
||||
* @param array $array
|
||||
* @param callable $callback
|
||||
* @return array
|
||||
*/
|
||||
public static function filter(array $array, callable $callback): array
|
||||
{
|
||||
@@ -766,19 +738,16 @@ class A
|
||||
/**
|
||||
* Remove key(s) from an array
|
||||
* @since 3.6.5
|
||||
*
|
||||
* @param array $array
|
||||
* @param int|string|array $keys
|
||||
* @return array
|
||||
*/
|
||||
public static function without(array $array, $keys): array
|
||||
public static function without(array $array, int|string|array $keys): array
|
||||
{
|
||||
if (is_int($keys) || is_string($keys)) {
|
||||
$keys = static::wrap($keys);
|
||||
}
|
||||
|
||||
return static::filter($array, function ($value, $key) use ($keys) {
|
||||
return in_array($key, $keys, true) === false;
|
||||
});
|
||||
return static::filter(
|
||||
$array,
|
||||
fn ($value, $key) => in_array($key, $keys, true) === false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1175,11 +1175,7 @@ class Collection extends Iterator implements Countable
|
||||
return $callback->call($this, $condition);
|
||||
}
|
||||
|
||||
if ($fallback !== null) {
|
||||
return $fallback->call($this, $condition);
|
||||
}
|
||||
|
||||
return $this;
|
||||
return $fallback?->call($this, $condition) ?? $this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace Kirby\Toolkit;
|
||||
|
||||
use ArgumentCountError;
|
||||
use Closure;
|
||||
use Kirby\Exception\Exception;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Filesystem\F;
|
||||
@@ -179,17 +180,17 @@ class Component
|
||||
protected function applyProps(array $props): void
|
||||
{
|
||||
foreach ($props as $propName => $propFunction) {
|
||||
if (is_a($propFunction, 'Closure') === true) {
|
||||
if ($propFunction instanceof Closure) {
|
||||
if (isset($this->attrs[$propName]) === true) {
|
||||
try {
|
||||
$this->$propName = $this->props[$propName] = $propFunction->call($this, $this->attrs[$propName]);
|
||||
} catch (TypeError $e) {
|
||||
} catch (TypeError) {
|
||||
throw new TypeError('Invalid value for "' . $propName . '"');
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$this->$propName = $this->props[$propName] = $propFunction->call($this);
|
||||
} catch (ArgumentCountError $e) {
|
||||
} catch (ArgumentCountError) {
|
||||
throw new ArgumentCountError('Please provide a value for "' . $propName . '"');
|
||||
}
|
||||
}
|
||||
@@ -209,7 +210,7 @@ class Component
|
||||
protected function applyComputed(array $computed): void
|
||||
{
|
||||
foreach ($computed as $computedName => $computedFunction) {
|
||||
if (is_a($computedFunction, 'Closure') === true) {
|
||||
if ($computedFunction instanceof Closure) {
|
||||
$this->$computedName = $this->computed[$computedName] = $computedFunction->call($this);
|
||||
}
|
||||
}
|
||||
@@ -283,7 +284,7 @@ class Component
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
if (is_a($this->options['toArray'] ?? null, 'Closure') === true) {
|
||||
if (($this->options['toArray'] ?? null) instanceof Closure) {
|
||||
return $this->options['toArray']->call($this);
|
||||
}
|
||||
|
||||
|
@@ -58,7 +58,7 @@ class Controller
|
||||
|
||||
$function = F::load($file);
|
||||
|
||||
if (is_a($function, 'Closure') === false) {
|
||||
if ($function instanceof Closure === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace Kirby\Toolkit;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
@@ -32,7 +33,7 @@ class Date extends DateTime
|
||||
$datetime = date('r', $datetime);
|
||||
}
|
||||
|
||||
if (is_a($datetime, 'DateTimeInterface') === true) {
|
||||
if ($datetime instanceof DateTimeInterface) {
|
||||
$datetime = $datetime->format('r');
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ class Date extends DateTime
|
||||
* @param int|null $day
|
||||
* @return int
|
||||
*/
|
||||
public function day(?int $day = null): int
|
||||
public function day(int|null $day = null): int
|
||||
{
|
||||
if ($day === null) {
|
||||
return (int)$this->format('d');
|
||||
@@ -126,7 +127,7 @@ class Date extends DateTime
|
||||
* @param int|null $hour
|
||||
* @return int
|
||||
*/
|
||||
public function hour(?int $hour = null): int
|
||||
public function hour(int|null $hour = null): int
|
||||
{
|
||||
if ($hour === null) {
|
||||
return (int)$this->format('H');
|
||||
@@ -234,7 +235,7 @@ class Date extends DateTime
|
||||
* @param int|null $minute
|
||||
* @return int
|
||||
*/
|
||||
public function minute(?int $minute = null): int
|
||||
public function minute(int|null $minute = null): int
|
||||
{
|
||||
if ($minute === null) {
|
||||
return (int)$this->format('i');
|
||||
@@ -250,7 +251,7 @@ class Date extends DateTime
|
||||
* @param int|null $month
|
||||
* @return int
|
||||
*/
|
||||
public function month(?int $month = null): int
|
||||
public function month(int|null $month = null): int
|
||||
{
|
||||
if ($month === null) {
|
||||
return (int)$this->format('m');
|
||||
@@ -305,7 +306,7 @@ class Date extends DateTime
|
||||
* @param \DateTimeZone|null $timezone
|
||||
* @return static|null
|
||||
*/
|
||||
public static function optional(?string $datetime = null, ?DateTimeZone $timezone = null)
|
||||
public static function optional(string|null $datetime = null, ?DateTimeZone $timezone = null)
|
||||
{
|
||||
if (empty($datetime) === true) {
|
||||
return null;
|
||||
@@ -313,7 +314,7 @@ class Date extends DateTime
|
||||
|
||||
try {
|
||||
return new static($datetime, $timezone);
|
||||
} catch (Exception $e) {
|
||||
} catch (Exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -368,7 +369,7 @@ class Date extends DateTime
|
||||
* @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
|
||||
public static function roundedTimestamp(string|null $date = null, $step = null): int|null
|
||||
{
|
||||
if ($date = static::optional($date)) {
|
||||
if ($step !== null) {
|
||||
@@ -391,7 +392,7 @@ class Date extends DateTime
|
||||
* @param int|null $second
|
||||
* @return int
|
||||
*/
|
||||
public function second(?int $second = null): int
|
||||
public function second(int|null $second = null): int
|
||||
{
|
||||
if ($second === null) {
|
||||
return (int)$this->format('s');
|
||||
@@ -421,7 +422,7 @@ class Date extends DateTime
|
||||
* @param array|null $default Default values to use if one or both values are not provided
|
||||
* @return array
|
||||
*/
|
||||
public static function stepConfig($input = null, ?array $default = null): array
|
||||
public static function stepConfig($input = null, array|null $default = null): array
|
||||
{
|
||||
$default ??= [
|
||||
'size' => 1,
|
||||
@@ -502,19 +503,12 @@ class Date extends DateTime
|
||||
*/
|
||||
public function toString(string $mode = 'datetime', bool $timezone = true): string
|
||||
{
|
||||
switch ($mode) {
|
||||
case 'date':
|
||||
$format = 'Y-m-d';
|
||||
break;
|
||||
case 'time':
|
||||
$format = 'H:i:s';
|
||||
break;
|
||||
case 'datetime':
|
||||
$format = 'Y-m-d H:i:s';
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException('Invalid mode');
|
||||
}
|
||||
$format = match ($mode) {
|
||||
'date' => 'Y-m-d',
|
||||
'time' => 'H:i:s',
|
||||
'datetime' => 'Y-m-d H:i:s',
|
||||
default => throw new InvalidArgumentException('Invalid mode')
|
||||
};
|
||||
|
||||
if ($timezone === true) {
|
||||
$format .= 'P';
|
||||
@@ -529,7 +523,7 @@ class Date extends DateTime
|
||||
* @param int|null $year
|
||||
* @return int
|
||||
*/
|
||||
public function year(?int $year = null): int
|
||||
public function year(int|null $year = null): int
|
||||
{
|
||||
if ($year === null) {
|
||||
return (int)$this->format('Y');
|
||||
|
@@ -9,6 +9,7 @@ use DOMDocumentType;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use DOMProcessingInstruction;
|
||||
use DOMText;
|
||||
use DOMXPath;
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Exception\Exception;
|
||||
@@ -68,12 +69,6 @@ class Dom
|
||||
$this->doc = new DOMDocument();
|
||||
|
||||
$loaderSetting = null;
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
// prevent loading external entities to protect against XXE attacks;
|
||||
// only needed for PHP versions before 8.0 (the function was deprecated
|
||||
// as the disabled state is the new default in PHP 8.0+)
|
||||
$loaderSetting = libxml_disable_entity_loader(true);
|
||||
}
|
||||
|
||||
// switch to "user error handling"
|
||||
$intErrorsSetting = libxml_use_internal_errors(true);
|
||||
@@ -106,12 +101,6 @@ class Dom
|
||||
$load = $this->doc->loadXML($code);
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
// ensure that we don't alter global state by
|
||||
// resetting the original value
|
||||
libxml_disable_entity_loader($loaderSetting);
|
||||
}
|
||||
|
||||
// get one error for use below and reset the global state
|
||||
$error = libxml_get_last_error();
|
||||
libxml_clear_errors();
|
||||
@@ -442,7 +431,7 @@ class Dom
|
||||
* @param \Closure|null Comparison callback that returns whether the expected and real name match
|
||||
* @return string|false Matched name in the list or `false`
|
||||
*/
|
||||
public static function listContainsName(array $list, DOMNode $node, array $options, ?Closure $compare = null)
|
||||
public static function listContainsName(array $list, DOMNode $node, array $options, Closure|null $compare = null)
|
||||
{
|
||||
$allowedNamespaces = $options['allowedNamespaces'];
|
||||
$localName = $node->localName;
|
||||
@@ -589,7 +578,7 @@ class Dom
|
||||
// convert the `DOMNodeList` to an array first, otherwise removing
|
||||
// nodes would shift the list and make subsequent operations fail
|
||||
foreach (iterator_to_array($this->doc->childNodes, false) as $child) {
|
||||
if (is_a($child, 'DOMDocumentType') === true) {
|
||||
if ($child instanceof DOMDocumentType) {
|
||||
$this->sanitizeDoctype($child, $options, $errors);
|
||||
}
|
||||
}
|
||||
@@ -646,7 +635,7 @@ class Dom
|
||||
foreach ($node->childNodes as $childNode) {
|
||||
// discard text nodes as they can be unexpected
|
||||
// directly in the parent element
|
||||
if (is_a($childNode, 'DOMText') === true) {
|
||||
if ($childNode instanceof DOMText) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@@ -137,7 +137,7 @@ class Html extends Xml
|
||||
* @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 $before = null, ?string $after = null): ?string
|
||||
public static function attr($name, $value = null, string|null $before = null, string|null $after = null): string|null
|
||||
{
|
||||
// HTML supports boolean attributes without values
|
||||
if (is_array($name) === false && is_bool($value) === true) {
|
||||
@@ -219,7 +219,7 @@ class Html extends Xml
|
||||
*
|
||||
* @psalm-suppress ParamNameMismatch
|
||||
*/
|
||||
public static function encode(?string $string, bool $keepTags = false): string
|
||||
public static function encode(string|null $string, bool $keepTags = false): string
|
||||
{
|
||||
if ($string === null) {
|
||||
return '';
|
||||
@@ -279,7 +279,7 @@ class Html extends Xml
|
||||
* @param array $attr Additional attributes for the `<script>` tag
|
||||
* @return string The generated HTML
|
||||
*/
|
||||
public static function gist(string $url, ?string $file = null, array $attr = []): string
|
||||
public static function gist(string $url, string|null $file = null, array $attr = []): string
|
||||
{
|
||||
if ($file === null) {
|
||||
$src = $url . '.js';
|
||||
@@ -363,7 +363,7 @@ class Html extends Xml
|
||||
* @param string|null $target Current `target` value
|
||||
* @return string|null New `rel` value or `null` if not needed
|
||||
*/
|
||||
public static function rel(?string $rel = null, ?string $target = null): ?string
|
||||
public static function rel(string|null $rel = null, string|null $target = null): string|null
|
||||
{
|
||||
$rel = trim($rel ?? '');
|
||||
|
||||
@@ -430,7 +430,7 @@ class Html extends Xml
|
||||
* @param mixed $value
|
||||
* @return string|null
|
||||
*/
|
||||
public static function value($value): ?string
|
||||
public static function value($value): string|null
|
||||
{
|
||||
if ($value === true) {
|
||||
return 'true';
|
||||
@@ -462,7 +462,7 @@ class Html extends Xml
|
||||
* @param array $attr Additional attributes for the `<iframe>` tag
|
||||
* @return string|null The generated HTML
|
||||
*/
|
||||
public static function video(string $url, array $options = [], array $attr = []): ?string
|
||||
public static function video(string $url, array $options = [], array $attr = []): string|null
|
||||
{
|
||||
// YouTube video
|
||||
if (Str::contains($url, 'youtu', true) === true) {
|
||||
@@ -522,22 +522,17 @@ class Html extends Xml
|
||||
* @param array $attr Additional attributes for the `<iframe>` tag
|
||||
* @return string|null The generated HTML
|
||||
*/
|
||||
public static function vimeo(string $url, array $options = [], array $attr = []): ?string
|
||||
public static function vimeo(string $url, array $options = [], array $attr = []): string|null
|
||||
{
|
||||
$uri = new Uri($url);
|
||||
$path = $uri->path();
|
||||
$query = $uri->query();
|
||||
$id = null;
|
||||
|
||||
switch ($uri->host()) {
|
||||
case 'vimeo.com':
|
||||
case 'www.vimeo.com':
|
||||
$id = $path->last();
|
||||
break;
|
||||
case 'player.vimeo.com':
|
||||
$id = $path->nth(1);
|
||||
break;
|
||||
}
|
||||
$id = match ($uri->host()) {
|
||||
'vimeo.com', 'www.vimeo.com' => $path->last(),
|
||||
'player.vimeo.com' => $path->nth(1),
|
||||
default => null
|
||||
};
|
||||
|
||||
if (empty($id) === true || preg_match('!^[0-9]*$!', $id) !== 1) {
|
||||
return null;
|
||||
@@ -562,7 +557,7 @@ class Html extends Xml
|
||||
* @param array $attr Additional attributes for the `<iframe>` tag
|
||||
* @return string|null The generated HTML
|
||||
*/
|
||||
public static function youtube(string $url, array $options = [], array $attr = []): ?string
|
||||
public static function youtube(string $url, array $options = [], array $attr = []): string|null
|
||||
{
|
||||
if (preg_match('!youtu!i', $url) !== 1) {
|
||||
return null;
|
||||
@@ -576,7 +571,7 @@ class Html extends Xml
|
||||
$host = 'https://' . $uri->host() . '/embed';
|
||||
$src = null;
|
||||
|
||||
$isYoutubeId = function (?string $id = null): bool {
|
||||
$isYoutubeId = function (string|null $id = null): bool {
|
||||
if (empty($id) === true) {
|
||||
return false;
|
||||
}
|
||||
|
@@ -99,13 +99,9 @@ class I18n
|
||||
*/
|
||||
public static function formatNumber($number, string $locale = null): string
|
||||
{
|
||||
$locale ??= static::locale();
|
||||
|
||||
$formatter = static::decimalNumberFormatter($locale);
|
||||
if ($formatter !== null) {
|
||||
$number = $formatter->format($number);
|
||||
}
|
||||
|
||||
$locale ??= static::locale();
|
||||
$formatter = static::decimalNumberFormatter($locale);
|
||||
$number = $formatter?->format($number) ?? $number;
|
||||
return (string)$number;
|
||||
}
|
||||
|
||||
@@ -142,18 +138,25 @@ class I18n
|
||||
|
||||
if (is_array($key) === true) {
|
||||
// try to use actual locale
|
||||
if (isset($key[$locale])) {
|
||||
if (isset($key[$locale]) === true) {
|
||||
return $key[$locale];
|
||||
}
|
||||
// try to use language code, e.g. `es` when locale is `es_ES`
|
||||
$lang = Str::before($locale, '_');
|
||||
if (isset($key[$lang])) {
|
||||
if (isset($key[$lang]) === true) {
|
||||
return $key[$lang];
|
||||
}
|
||||
// use fallback
|
||||
if (is_array($fallback)) {
|
||||
return $fallback[$locale] ?? $fallback['en'] ?? reset($fallback);
|
||||
// use global wildcard as i18n key
|
||||
if (isset($key['*']) === true) {
|
||||
return static::translate($key['*'], $key['*']);
|
||||
}
|
||||
// use fallback
|
||||
if (is_array($fallback) === true) {
|
||||
return $fallback[$locale] ??
|
||||
$fallback['en'] ??
|
||||
reset($fallback);
|
||||
}
|
||||
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
@@ -189,7 +192,7 @@ class I18n
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
public static function template(string $key, $fallback = null, ?array $replace = null, ?string $locale = null): string
|
||||
public static function template(string $key, $fallback = null, array|null $replace = null, string|null $locale = null): string
|
||||
{
|
||||
if (is_array($fallback) === true) {
|
||||
$replace = $fallback;
|
||||
@@ -209,9 +212,6 @@ class I18n
|
||||
* Returns the current or any other translation
|
||||
* by locale. If the translation does not exist
|
||||
* yet, the loader will try to load it, if defined.
|
||||
*
|
||||
* @param string|null $locale
|
||||
* @return array
|
||||
*/
|
||||
public static function translation(string $locale = null): array
|
||||
{
|
||||
@@ -221,7 +221,7 @@ class I18n
|
||||
return static::$translations[$locale];
|
||||
}
|
||||
|
||||
if (is_a(static::$load, 'Closure') === true) {
|
||||
if (static::$load instanceof Closure) {
|
||||
return static::$translations[$locale] = (static::$load)($locale);
|
||||
}
|
||||
|
||||
@@ -236,8 +236,6 @@ class I18n
|
||||
|
||||
/**
|
||||
* Returns all loaded or defined translations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function translations(): array
|
||||
{
|
||||
@@ -246,16 +244,17 @@ class I18n
|
||||
|
||||
/**
|
||||
* Returns (and creates) a decimal number formatter for a given locale
|
||||
*
|
||||
* @return \NumberFormatter|null
|
||||
*/
|
||||
protected static function decimalNumberFormatter(string $locale): ?NumberFormatter
|
||||
protected static function decimalNumberFormatter(string $locale): NumberFormatter|null
|
||||
{
|
||||
if (isset(static::$decimalsFormatters[$locale])) {
|
||||
if (isset(static::$decimalsFormatters[$locale]) === true) {
|
||||
return static::$decimalsFormatters[$locale];
|
||||
}
|
||||
|
||||
if (extension_loaded('intl') !== true || class_exists('NumberFormatter') !== true) {
|
||||
if (
|
||||
extension_loaded('intl') !== true ||
|
||||
class_exists('NumberFormatter') !== true
|
||||
) {
|
||||
return null; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
@@ -287,7 +286,7 @@ class I18n
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_a($translation, 'Closure') === true) {
|
||||
if ($translation instanceof Closure) {
|
||||
return $translation($count);
|
||||
}
|
||||
|
||||
|
@@ -75,7 +75,7 @@ class Pagination
|
||||
|
||||
$params = [];
|
||||
|
||||
if (is_a($a, static::class) === true) {
|
||||
if ($a instanceof static) {
|
||||
/**
|
||||
* First argument is a pagination/self object
|
||||
*/
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Kirby\Toolkit;
|
||||
|
||||
use Closure;
|
||||
use Kirby\Exception\BadMethodCallException;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
|
||||
@@ -48,7 +49,7 @@ class Query
|
||||
* @param string|null $query
|
||||
* @param array|object $data
|
||||
*/
|
||||
public function __construct(?string $query = null, $data = [])
|
||||
public function __construct(string|null $query = null, $data = [])
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->data = $data;
|
||||
@@ -98,7 +99,7 @@ class Query
|
||||
if (array_key_exists($method, $data) === true) {
|
||||
$value = $data[$method];
|
||||
|
||||
if (is_a($value, 'Closure') === true) {
|
||||
if ($value instanceof Closure) {
|
||||
$value = $value(...$args);
|
||||
} elseif ($args !== []) {
|
||||
throw new InvalidArgumentException('Cannot access array element ' . $method . ' with arguments');
|
||||
|
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace Kirby\Toolkit;
|
||||
|
||||
use Closure;
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use IntlDateFormatter;
|
||||
use Kirby\Cms\Helpers;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
@@ -326,14 +326,14 @@ class Str
|
||||
* @param string $handler date, intl or strftime
|
||||
* @return string|int
|
||||
*/
|
||||
public static function date(?int $time = null, $format = null, string $handler = 'date')
|
||||
public static function date(int|null $time = null, $format = null, string $handler = 'date')
|
||||
{
|
||||
if (is_null($format) === true) {
|
||||
return $time;
|
||||
}
|
||||
|
||||
// $format is an IntlDateFormatter instance
|
||||
if (is_a($format, 'IntlDateFormatter') === true) {
|
||||
if ($format instanceof IntlDateFormatter) {
|
||||
return $format->format($time ?? time());
|
||||
}
|
||||
|
||||
@@ -469,7 +469,12 @@ class Str
|
||||
public static function excerpt($string, $chars = 140, $strip = true, $rep = ' …')
|
||||
{
|
||||
if ($strip === true) {
|
||||
$string = strip_tags(str_replace('<', ' <', $string));
|
||||
// ensure that opening tags are preceded by a space, so that
|
||||
// when tags are skipped we can be sure that words stay separate
|
||||
$string = preg_replace('#\s*<([^\/])#', ' <${1}', $string);
|
||||
|
||||
// in strip mode, we always return plain text
|
||||
$string = strip_tags($string);
|
||||
}
|
||||
|
||||
// replace line breaks with spaces
|
||||
@@ -552,21 +557,6 @@ class Str
|
||||
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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string to kebab case.
|
||||
*
|
||||
@@ -629,23 +619,14 @@ class Str
|
||||
$pool = array_merge($pool, static::pool($t));
|
||||
}
|
||||
} else {
|
||||
switch (strtolower($type)) {
|
||||
case 'alphalower':
|
||||
$pool = range('a', 'z');
|
||||
break;
|
||||
case 'alphaupper':
|
||||
$pool = range('A', 'Z');
|
||||
break;
|
||||
case 'alpha':
|
||||
$pool = static::pool(['alphaLower', 'alphaUpper']);
|
||||
break;
|
||||
case 'num':
|
||||
$pool = range(0, 9);
|
||||
break;
|
||||
case 'alphanum':
|
||||
$pool = static::pool(['alpha', 'num']);
|
||||
break;
|
||||
}
|
||||
$pool = match (strtolower($type)) {
|
||||
'alphalower' => range('a', 'z'),
|
||||
'alphaupper' => range('A', 'Z'),
|
||||
'alpha' => static::pool(['alphaLower', 'alphaUpper']),
|
||||
'num' => range(0, 9),
|
||||
'alphanum' => static::pool(['alpha', 'num']),
|
||||
default => $pool
|
||||
};
|
||||
}
|
||||
|
||||
return $array ? $pool : implode('', $pool);
|
||||
@@ -735,19 +716,20 @@ class Str
|
||||
* defaults to no limit
|
||||
* @return string|array String with replaced values;
|
||||
* if $string is an array, array of strings
|
||||
* @psalm-return ($string is array ? array : string)
|
||||
*/
|
||||
public static function replace($string, $search, $replace, $limit = -1)
|
||||
{
|
||||
// convert Kirby collections to arrays
|
||||
if (is_a($string, 'Kirby\Toolkit\Collection') === true) {
|
||||
if ($string instanceof Collection) {
|
||||
$string = $string->toArray();
|
||||
}
|
||||
|
||||
if (is_a($search, 'Kirby\Toolkit\Collection') === true) {
|
||||
if ($search instanceof Collection) {
|
||||
$search = $search->toArray();
|
||||
}
|
||||
|
||||
if (is_a($replace, 'Kirby\Toolkit\Collection') === true) {
|
||||
if ($replace instanceof Collection) {
|
||||
$replace = $replace->toArray();
|
||||
}
|
||||
|
||||
@@ -901,8 +883,8 @@ class Str
|
||||
*/
|
||||
public static function safeTemplate(string $string = null, array $data = [], array $options = []): string
|
||||
{
|
||||
$callback = is_a(($options['callback'] ?? null), 'Closure') === true ? $options['callback'] : null;
|
||||
$fallback = $options['fallback'] ?? '';
|
||||
$callback = ($options['callback'] ?? null) instanceof Closure ? $options['callback'] : null;
|
||||
$fallback = $options['fallback'] ?? null;
|
||||
|
||||
// replace and escape
|
||||
$string = static::template($string, $data, [
|
||||
@@ -1099,7 +1081,7 @@ class Str
|
||||
* and it has a built-in way to skip values
|
||||
* which are too short.
|
||||
*
|
||||
* @param string $string The string to split
|
||||
* @param string|array|null $string The string to split
|
||||
* @param string $separator The string to split by
|
||||
* @param int $length The min length of values.
|
||||
* @return array An array of found values
|
||||
@@ -1192,7 +1174,7 @@ class Str
|
||||
public static function template(string $string = null, array $data = [], array $options = []): string
|
||||
{
|
||||
$fallback = $options['fallback'] ?? null;
|
||||
$callback = is_a(($options['callback'] ?? null), 'Closure') === true ? $options['callback'] : null;
|
||||
$callback = ($options['callback'] ?? null) instanceof Closure ? $options['callback'] : null;
|
||||
$start = (string)($options['start'] ?? '{{');
|
||||
$end = (string)($options['end'] ?? '}}');
|
||||
|
||||
@@ -1206,7 +1188,7 @@ class Str
|
||||
if (strpos($query, '.') !== false) {
|
||||
try {
|
||||
$result = (new Query($match[1], $data))->result();
|
||||
} catch (Exception $e) {
|
||||
} catch (Exception) {
|
||||
$result = null;
|
||||
}
|
||||
} else {
|
||||
@@ -1220,7 +1202,14 @@ class Str
|
||||
|
||||
// callback on result if given
|
||||
if ($callback !== null) {
|
||||
$result = $callback((string)$result, $query, $data);
|
||||
$callbackResult = $callback((string)$result, $query, $data);
|
||||
|
||||
if ($result === null && $callbackResult === '') {
|
||||
// the empty string came just from string casting,
|
||||
// keep the null value and ignore the callback result
|
||||
} else {
|
||||
$result = $callbackResult;
|
||||
}
|
||||
}
|
||||
|
||||
// if we still don't have a result, keep the original placeholder
|
||||
@@ -1268,21 +1257,13 @@ class Str
|
||||
$type = gettype($type);
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'array':
|
||||
return (array)$string;
|
||||
case 'bool':
|
||||
case 'boolean':
|
||||
return filter_var($string, FILTER_VALIDATE_BOOLEAN);
|
||||
case 'double':
|
||||
case 'float':
|
||||
return (float)$string;
|
||||
case 'int':
|
||||
case 'integer':
|
||||
return (int)$string;
|
||||
}
|
||||
|
||||
return (string)$string;
|
||||
return match ($type) {
|
||||
'array' => (array)$string,
|
||||
'bool', 'boolean' => filter_var($string, FILTER_VALIDATE_BOOLEAN),
|
||||
'double', 'float' => (float)$string,
|
||||
'int', 'integer' => (int)$string,
|
||||
default => (string)$string
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -24,7 +24,7 @@ class Tpl
|
||||
* @return string
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function load(?string $file = null, array $data = []): string
|
||||
public static function load(string|null $file = null, array $data = []): string
|
||||
{
|
||||
if ($file === null || is_file($file) === false) {
|
||||
return '';
|
||||
|
@@ -2,9 +2,12 @@
|
||||
|
||||
namespace Kirby\Toolkit;
|
||||
|
||||
use Countable;
|
||||
use Exception;
|
||||
use Kirby\Cms\Field;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
use Kirby\Http\Idn;
|
||||
use Kirby\Uuid\Uuid;
|
||||
use ReflectionFunction;
|
||||
use Throwable;
|
||||
|
||||
@@ -121,7 +124,7 @@ class V
|
||||
* @param mixed ...$params
|
||||
* @return string|null
|
||||
*/
|
||||
public static function message(string $validatorName, ...$params): ?string
|
||||
public static function message(string $validatorName, ...$params): string|null
|
||||
{
|
||||
$validatorName = strtolower($validatorName);
|
||||
$translationKey = 'error.validation.' . $validatorName;
|
||||
@@ -146,7 +149,7 @@ class V
|
||||
}
|
||||
}
|
||||
$value = implode(', ', $value);
|
||||
} catch (Throwable $e) {
|
||||
} catch (Throwable) {
|
||||
$value = '-';
|
||||
}
|
||||
}
|
||||
@@ -317,7 +320,7 @@ V::$validators = [
|
||||
* Pass an operator as second argument and another date as
|
||||
* third argument to compare them.
|
||||
*/
|
||||
'date' => function (?string $value, string $operator = null, string $test = null): bool {
|
||||
'date' => function (string|null $value, string $operator = null, string $test = null): bool {
|
||||
// make sure $value is a string
|
||||
$value ??= '';
|
||||
|
||||
@@ -338,22 +341,16 @@ V::$validators = [
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($operator) {
|
||||
case '!=':
|
||||
return $value !== $test;
|
||||
case '<':
|
||||
return $value < $test;
|
||||
case '>':
|
||||
return $value > $test;
|
||||
case '<=':
|
||||
return $value <= $test;
|
||||
case '>=':
|
||||
return $value >= $test;
|
||||
case '==':
|
||||
return $value === $test;
|
||||
}
|
||||
return match ($operator) {
|
||||
'!=' => $value !== $test,
|
||||
'<' => $value < $test,
|
||||
'>' => $value > $test,
|
||||
'<=' => $value <= $test,
|
||||
'>=' => $value >= $test,
|
||||
'==' => $value === $test,
|
||||
|
||||
throw new InvalidArgumentException('Invalid date comparison operator: "' . $operator . '". Allowed operators: "==", "!=", "<", "<=", ">", ">="');
|
||||
default => throw new InvalidArgumentException('Invalid date comparison operator: "' . $operator . '". Allowed operators: "==", "!=", "<", "<=", ">", ">="')
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -380,7 +377,7 @@ V::$validators = [
|
||||
if (filter_var($value, FILTER_VALIDATE_EMAIL) === false) {
|
||||
try {
|
||||
$email = Idn::encodeEmail($value);
|
||||
} catch (Throwable $e) {
|
||||
} catch (Throwable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -579,7 +576,7 @@ V::$validators = [
|
||||
'size' => function ($value, $size, $operator = '=='): bool {
|
||||
// if value is field object, first convert it to a readable value
|
||||
// it is important to check at the beginning as the value can be string or numeric
|
||||
if (is_a($value, '\Kirby\Cms\Field') === true) {
|
||||
if ($value instanceof Field) {
|
||||
$value = $value->value();
|
||||
}
|
||||
|
||||
@@ -590,7 +587,7 @@ V::$validators = [
|
||||
} elseif (is_array($value) === true) {
|
||||
$count = count($value);
|
||||
} elseif (is_object($value) === true) {
|
||||
if ($value instanceof \Countable) {
|
||||
if ($value instanceof Countable) {
|
||||
$count = count($value);
|
||||
} elseif (method_exists($value, 'count') === true) {
|
||||
$count = $value->count();
|
||||
@@ -601,18 +598,13 @@ V::$validators = [
|
||||
throw new Exception('$value is of type without size');
|
||||
}
|
||||
|
||||
switch ($operator) {
|
||||
case '<':
|
||||
return $count < $size;
|
||||
case '>':
|
||||
return $count > $size;
|
||||
case '<=':
|
||||
return $count <= $size;
|
||||
case '>=':
|
||||
return $count >= $size;
|
||||
default:
|
||||
return $count == $size;
|
||||
}
|
||||
return match ($operator) {
|
||||
'<' => $count < $size,
|
||||
'>' => $count > $size,
|
||||
'<=' => $count <= $size,
|
||||
'>=' => $count >= $size,
|
||||
default => $count == $size
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -637,5 +629,12 @@ V::$validators = [
|
||||
// Added localhost support and removed 127.*.*.* ip restriction
|
||||
$regex = '_^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:localhost)|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$_iu';
|
||||
return preg_match($regex, $value ?? '') !== 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks for a valid Uuid, optionally for specific model type
|
||||
*/
|
||||
'uuid' => function (string $value, string $type = null): bool {
|
||||
return Uuid::is($value, $type);
|
||||
}
|
||||
];
|
||||
|
@@ -71,7 +71,7 @@ class Xml
|
||||
* If used with a `$name` array, this can be set to `false` to disable attribute sorting.
|
||||
* @return string|null The generated XML attributes string
|
||||
*/
|
||||
public static function attr($name, $value = null): ?string
|
||||
public static function attr($name, $value = null): string|null
|
||||
{
|
||||
if (is_array($name) === true) {
|
||||
if ($value !== false) {
|
||||
@@ -198,7 +198,7 @@ class Xml
|
||||
* @param string|null $string
|
||||
* @return string
|
||||
*/
|
||||
public static function decode(?string $string): string
|
||||
public static function decode(string|null $string): string
|
||||
{
|
||||
if ($string === null) {
|
||||
$string = '';
|
||||
@@ -223,7 +223,7 @@ class Xml
|
||||
* @param bool $html True = Convert to HTML-safe first
|
||||
* @return string
|
||||
*/
|
||||
public static function encode(?string $string, bool $html = true): string
|
||||
public static function encode(string|null $string, bool $html = true): string
|
||||
{
|
||||
if ($string === null) {
|
||||
return '';
|
||||
@@ -256,7 +256,7 @@ class Xml
|
||||
* @param string $xml
|
||||
* @return array|null Parsed array or `null` on error
|
||||
*/
|
||||
public static function parse(string $xml): ?array
|
||||
public static function parse(string $xml): array|null
|
||||
{
|
||||
$xml = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);
|
||||
|
||||
@@ -366,7 +366,7 @@ class Xml
|
||||
* @param int $level Indentation level
|
||||
* @return string The generated XML
|
||||
*/
|
||||
public static function tag(string $name, $content = '', array $attr = null, ?string $indent = null, int $level = 0): string
|
||||
public static function tag(string $name, $content = '', array $attr = null, string|null $indent = null, int $level = 0): string
|
||||
{
|
||||
$attr = static::attr($attr);
|
||||
$start = '<' . $name . ($attr ? ' ' . $attr : '') . '>';
|
||||
@@ -399,7 +399,7 @@ class Xml
|
||||
* @param mixed $value
|
||||
* @return string|null
|
||||
*/
|
||||
public static function value($value): ?string
|
||||
public static function value($value): string|null
|
||||
{
|
||||
if ($value === true) {
|
||||
return 'true';
|
||||
|
Reference in New Issue
Block a user