Upgrade to 3.6.1

This commit is contained in:
Bastian Allgeier
2021-12-07 12:39:37 +01:00
parent 9fc43ea22c
commit 70b8439c49
134 changed files with 19987 additions and 1100 deletions

View File

@@ -162,7 +162,7 @@ class Api
*/
public function call(string $path = null, string $method = 'GET', array $requestData = [])
{
$path = rtrim($path, '/');
$path = rtrim($path ?? '', '/');
$this->setRequestMethod($method);
$this->setRequestData($requestData);

View File

@@ -330,6 +330,10 @@ class App
{
$router = $this->router();
/**
* @todo Closures should not defined statically but
* for each instance to avoid this constant setting and unsetting
*/
$router::$beforeEach = function ($route, $path, $method) {
$this->trigger('route:before', compact('route', 'path', 'method'));
};
@@ -338,7 +342,12 @@ class App
return $this->apply('route:after', compact('route', 'path', 'method', 'result', 'final'), 'result');
};
return $router->call($path ?? $this->path(), $method ?? $this->request()->method());
$result = $router->call($path ?? $this->path(), $method ?? $this->request()->method());
$router::$beforeEach = null;
$router::$afterEach = null;
return $result;
}
/**

View File

@@ -128,7 +128,7 @@ trait AppErrors
'code' => $code,
'message' => $exception->getMessage(),
'details' => $details,
'file' => ltrim($exception->getFile(), $_SERVER['DOCUMENT_ROOT'] ?? null),
'file' => ltrim($exception->getFile(), $_SERVER['DOCUMENT_ROOT'] ?? ''),
'line' => $exception->getLine(),
], $httpCode);
} else {
@@ -158,9 +158,23 @@ trait AppErrors
$whoops = $this->whoops();
$whoops->clearHandlers();
$whoops->pushHandler($handler);
$whoops->pushHandler($this->getExceptionHookWhoopsHandler());
$whoops->register(); // will only do something if not already registered
}
/**
* Initializes a callback handler for triggering the `system.exception` hook
*
* @return \Whoops\Handler\CallbackHandler
*/
protected function getExceptionHookWhoopsHandler(): CallbackHandler
{
return new CallbackHandler(function ($exception, $inspector, $run) {
$this->trigger('system.exception', compact('exception'));
return Handler::DONE;
});
}
/**
* Clears the Whoops handlers and disables Whoops
*

View File

@@ -605,9 +605,10 @@ class Auth
$originalLog = $log;
$time = time() - $this->kirby->option('auth.timeout', 3600);
foreach ($log as $category => $entries) {
$log[$category] = array_filter($entries, function ($entry) use ($time) {
return $entry['time'] > $time;
});
$log[$category] = array_filter(
$entries,
fn ($entry) => $entry['time'] > $time
);
}
// write new log to the file system if it changed

View File

@@ -74,7 +74,7 @@ class Blueprint
$props = $this->preset($props);
// normalize the name
$props['name'] = $props['name'] ?? 'default';
$props['name'] ??= 'default';
// normalize and translate the title
$props['title'] = $this->i18n($props['title'] ?? ucfirst($props['name']));
@@ -337,7 +337,7 @@ class Blueprint
$normalize = function ($props) use ($name) {
// inject the filename as name if no name is set
$props['name'] = $props['name'] ?? $name;
$props['name'] ??= $name;
// normalize the title
$title = $props['title'] ?? ucfirst($props['name']);
@@ -567,9 +567,7 @@ class Blueprint
// set all options to false
if ($options === false) {
return array_map(function () {
return false;
}, $defaults);
return array_map(fn () => false, $defaults);
}
// extend options if possible
@@ -579,7 +577,7 @@ class Blueprint
$alias = $aliases[$key] ?? null;
if ($alias !== null) {
$options[$alias] = $options[$alias] ?? $value;
$options[$alias] ??= $value;
unset($options[$key]);
}
}
@@ -765,9 +763,10 @@ class Blueprint
*/
public function sections(): array
{
return array_map(function ($section) {
return $this->section($section['name']);
}, $this->sections);
return A::map(
$this->sections,
fn ($section) => $this->section($section['name'])
);
}
/**

View File

@@ -208,7 +208,7 @@ class ContentLock
}
// add lock user to unlocked data
$this->data['unlock'] = $this->data['unlock'] ?? [];
$this->data['unlock'] ??= [];
$this->data['unlock'][] = $this->data['lock']['user'];
return $this->clearLock();

View File

@@ -208,7 +208,7 @@ class ContentTranslation
*/
public function slug(): ?string
{
return $this->slug = $this->slug ?? ($this->content()['slug'] ?? null);
return $this->slug ??= ($this->content()['slug'] ?? null);
}
/**

View File

@@ -167,7 +167,7 @@ class Core
*/
public function components(): array
{
return $this->cache['components'] ?? $this->cache['components'] = include $this->root . '/components.php';
return $this->cache['components'] ??= include $this->root . '/components.php';
}
/**
@@ -203,7 +203,7 @@ class Core
*/
public function fieldMethods(): array
{
return $this->cache['fieldMethods'] ?? $this->cache['fieldMethods'] = (include $this->root . '/methods.php')($this->kirby);
return $this->cache['fieldMethods'] ??= (include $this->root . '/methods.php')($this->kirby);
}
/**
@@ -295,7 +295,7 @@ class Core
*/
public function kirbyTags(): array
{
return $this->cache['kirbytags'] ?? $this->cache['kirbytags'] = include $this->root . '/tags.php';
return $this->cache['kirbytags'] ??= include $this->root . '/tags.php';
}
/**
@@ -321,7 +321,7 @@ class Core
*/
public function roots(): array
{
return $this->cache['roots'] ?? $this->cache['roots'] = [
return $this->cache['roots'] ??= [
// kirby
'kirby' => function (array $roots) {
return dirname(__DIR__, 2);
@@ -428,7 +428,7 @@ class Core
*/
public function routes(): array
{
return $this->cache['routes'] ?? $this->cache['routes'] = (include $this->root . '/routes.php')($this->kirby);
return $this->cache['routes'] ??= (include $this->root . '/routes.php')($this->kirby);
}
/**
@@ -517,7 +517,7 @@ class Core
*/
public function urls(): array
{
return $this->cache['urls'] ?? $this->cache['urls'] = [
return $this->cache['urls'] ??= [
'index' => function () {
return Url::index();
},

View File

@@ -3,6 +3,7 @@
namespace Kirby\Cms;
use Closure;
use Kirby\Toolkit\A;
use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Str;
@@ -42,7 +43,7 @@ class Fieldsets extends Items
$fieldset = Blueprint::extend($fieldset);
// make sure the type is always set
$fieldset['type'] = $fieldset['type'] ?? $type;
$fieldset['type'] ??= $type;
// extract groups
if ($fieldset['type'] === 'group') {
@@ -69,7 +70,7 @@ class Fieldsets extends Items
public static function factory(array $items = null, array $params = [])
{
$items = $items ?? option('blocks.fieldsets', [
$items ??= option('blocks.fieldsets', [
'code' => 'blocks/code',
'gallery' => 'blocks/gallery',
'heading' => 'blocks/heading',
@@ -94,8 +95,9 @@ class Fieldsets extends Items
public function toArray(?Closure $map = null): array
{
return array_map($map ?? function ($fieldset) {
return $fieldset->toArray();
}, $this->data);
return A::map(
$this->data,
$map ?? fn ($fieldset) => $fieldset->toArray()
);
}
}

View File

@@ -6,6 +6,7 @@ use Kirby\Filesystem\F;
use Kirby\Filesystem\IsFile;
use Kirby\Panel\File as Panel;
use Kirby\Toolkit\A;
use Kirby\Toolkit\Str;
/**
* The `$file` object provides a set
@@ -362,14 +363,9 @@ class File extends ModelWithContent
$file = $this->modifiedFile();
$content = $this->modifiedContent($languageCode);
$modified = max($file, $content);
$handler ??= $this->kirby()->option('date.handler', 'date');
if (is_null($format) === true) {
return $modified;
}
$handler = $handler ?? $this->kirby()->option('date.handler', 'date');
return $handler($format, $modified);
return Str::date($modified, $format, $handler);
}
/**
@@ -422,7 +418,7 @@ class File extends ModelWithContent
*/
public function parent()
{
return $this->parent = $this->parent ?? $this->kirby()->site();
return $this->parent ??= $this->kirby()->site();
}
/**
@@ -472,7 +468,7 @@ class File extends ModelWithContent
*/
public function root(): ?string
{
return $this->root = $this->root ?? $this->parent()->root() . '/' . $this->filename();
return $this->root ??= $this->parent()->root() . '/' . $this->filename();
}
/**
@@ -598,7 +594,7 @@ class File extends ModelWithContent
*/
public function template(): ?string
{
return $this->template = $this->template ?? $this->content()->get('template')->value();
return $this->template ??= $this->content()->get('template')->value();
}
/**
@@ -631,7 +627,7 @@ class File extends ModelWithContent
*/
public function url(): string
{
return $this->url ?? $this->url = ($this->kirby()->component('file::url'))($this->kirby(), $this);
return $this->url ??= ($this->kirby()->component('file::url'))($this->kirby(), $this);
}

View File

@@ -163,17 +163,24 @@ class FileBlueprint extends Blueprint
// normalize the MIME, extension and type from strings into arrays
if (is_string($accept['mime']) === true) {
$accept['mime'] = array_map(function ($mime) {
return $mime['value'];
}, Str::accepted($accept['mime']));
$accept['mime'] = array_map(
fn ($mime) => $mime['value'],
Str::accepted($accept['mime'])
);
}
if (is_string($accept['extension']) === true) {
$accept['extension'] = array_map('trim', explode(',', $accept['extension']));
$accept['extension'] = array_map(
'trim',
explode(',', $accept['extension'])
);
}
if (is_string($accept['type']) === true) {
$accept['type'] = array_map('trim', explode(',', $accept['type']));
$accept['type'] = array_map(
'trim',
explode(',', $accept['type'])
);
}
return $accept;

View File

@@ -203,9 +203,10 @@ trait FileModifications
if (
is_a($result, 'Kirby\Cms\FileVersion') === false &&
is_a($result, 'Kirby\Cms\File') === false
is_a($result, 'Kirby\Cms\File') === false &&
is_a($result, 'Kirby\Filesystem\Asset') === false
) {
throw new InvalidArgumentException('The file::version component must return a File or FileVersion object');
throw new InvalidArgumentException('The file::version component must return a File, FileVersion or Asset object');
}
return $result;

View File

@@ -87,9 +87,10 @@ class LanguageRouter
$patterns = A::wrap($route['pattern']);
// prefix all patterns with the page slug
$patterns = array_map(function ($pattern) use ($page, $language) {
return $page->uri($language) . '/' . $pattern;
}, $patterns);
$patterns = A::map(
$patterns,
fn ($pattern) => $page->uri($language) . '/' . $pattern
);
// re-inject the pattern and the full page object
$routes[$index]['pattern'] = $patterns;

View File

@@ -25,9 +25,10 @@ class Languages extends Collection
*/
public function __construct($objects = [], $parent = null)
{
$defaults = array_filter($objects, function ($language) {
return $language->isDefault() === true;
});
$defaults = array_filter(
$objects,
fn ($language) => $language->isDefault() === true
);
if (count($defaults) > 1) {
throw new DuplicateException('You cannot have multiple default languages. Please check your language config files.');
@@ -87,8 +88,9 @@ class Languages extends Collection
$props = F::load($file);
if (is_array($props) === true) {
// inject the language code from the filename if it does not exist
$props['code'] = $props['code'] ?? F::name($file);
// inject the language code from the filename
// if it does not exist
$props['code'] ??= F::name($file);
$languages[] = new Language($props);
}

View File

@@ -67,7 +67,7 @@ abstract class Model
*/
public function kirby()
{
return static::$kirby = static::$kirby ?? App::instance();
return static::$kirby ??= App::instance();
}
/**
@@ -77,7 +77,7 @@ abstract class Model
*/
public function site()
{
return $this->site = $this->site ?? $this->kirby()->site();
return $this->site ??= $this->kirby()->site();
}
/**

View File

@@ -382,7 +382,7 @@ class Page extends ModelWithContent
*/
public function depth(): int
{
return $this->depth = $this->depth ?? (substr_count($this->id(), '/') + 1);
return $this->depth ??= (substr_count($this->id(), '/') + 1);
}
/**
@@ -1102,7 +1102,7 @@ class Page extends ModelWithContent
*/
public function root(): string
{
return $this->root = $this->root ?? $this->kirby()->root('content') . '/' . $this->diruri();
return $this->root ??= $this->kirby()->root('content') . '/' . $this->diruri();
}
/**

View File

@@ -151,7 +151,7 @@ class Pages extends Collection
*/
public static function factory(array $pages, Model $model = null, bool $draft = false)
{
$model = $model ?? App::instance()->site();
$model ??= App::instance()->site();
$children = new static([], $model);
$kirby = $model->kirby();

View File

@@ -69,9 +69,9 @@ class Pagination extends BasePagination
$config = $kirby->option('pagination', []);
$request = $kirby->request();
$params['limit'] = $params['limit'] ?? $config['limit'] ?? 20;
$params['method'] = $params['method'] ?? $config['method'] ?? 'param';
$params['variable'] = $params['variable'] ?? $config['variable'] ?? 'page';
$params['limit'] ??= $config['limit'] ?? 20;
$params['method'] ??= $config['method'] ?? 'param';
$params['variable'] ??= $config['variable'] ?? 'page';
if (empty($params['url']) === true) {
$params['url'] = new Uri($kirby->url('current'), [
@@ -81,9 +81,9 @@ class Pagination extends BasePagination
}
if ($params['method'] === 'query') {
$params['page'] = $params['page'] ?? $params['url']->query()->get($params['variable']);
$params['page'] ??= $params['url']->query()->get($params['variable']);
} elseif ($params['method'] === 'param') {
$params['page'] = $params['page'] ?? $params['url']->params()->get($params['variable']);
$params['page'] ??= $params['url']->params()->get($params['variable']);
}
parent::__construct($params);

View File

@@ -210,7 +210,7 @@ class Role extends Model
*/
public function title(): string
{
return $this->title = $this->title ?? ucfirst($this->name());
return $this->title ??= ucfirst($this->name());
}
/**

View File

@@ -49,8 +49,8 @@ class Section extends Component
}
// use the type as fallback for the name
$attrs['name'] = $attrs['name'] ?? $type;
$attrs['type'] = $type;
$attrs['name'] ??= $type;
$attrs['type'] = $type;
parent::__construct($type, $attrs);
}

View File

@@ -463,7 +463,7 @@ class Site extends ModelWithContent
*/
public function root(): string
{
return $this->root = $this->root ?? $this->kirby()->root('content');
return $this->root ??= $this->kirby()->root('content');
}
/**

View File

@@ -405,7 +405,7 @@ class System
{
return
version_compare(PHP_VERSION, '7.4.0', '>=') === true &&
version_compare(PHP_VERSION, '8.1.0', '<') === true;
version_compare(PHP_VERSION, '8.2.0', '<') === true;
}
/**
@@ -508,7 +508,7 @@ class System
];
}
$software = $_SERVER['SERVER_SOFTWARE'] ?? null;
$software = $_SERVER['SERVER_SOFTWARE'] ?? '';
preg_match('!(' . implode('|', $servers) . ')!i', $software, $matches);

View File

@@ -78,7 +78,11 @@ class Template
*/
public function exists(): bool
{
return file_exists($this->file());
if ($file = $this->file()) {
return file_exists($file);
}
return false;
}
/**

View File

@@ -228,7 +228,7 @@ class User extends ModelWithContent
protected function credentials(): array
{
return $this->credentials = $this->credentials ?? $this->readCredentials();
return $this->credentials ??= $this->readCredentials();
}
/**
@@ -238,7 +238,7 @@ class User extends ModelWithContent
*/
public function email(): ?string
{
return $this->email = $this->email ?? $this->credentials()['email'] ?? null;
return $this->email ??= $this->credentials()['email'] ?? null;
}
/**
@@ -403,7 +403,7 @@ class User extends ModelWithContent
*/
public function language(): string
{
return $this->language ?? $this->language = $this->credentials()['language'] ?? $this->kirby()->panelLanguage();
return $this->language ??= $this->credentials()['language'] ?? $this->kirby()->panelLanguage();
}
/**
@@ -530,9 +530,9 @@ class User extends ModelWithContent
$modifiedContent = F::modified($this->contentFile($languageCode));
$modifiedIndex = F::modified($this->root() . '/index.php');
$modifiedTotal = max([$modifiedContent, $modifiedIndex]);
$handler = $handler ?? $this->kirby()->option('date.handler', 'date');
$handler ??= $this->kirby()->option('date.handler', 'date');
return $handler($format, $modifiedTotal);
return Str::date($modifiedTotal, $format, $handler);
}
/**

View File

@@ -65,7 +65,7 @@ class Data
static::$handlers[static::$aliases[$type] ?? null] ??
null;
if (class_exists($handler)) {
if ($handler !== null && class_exists($handler)) {
return new $handler();
}

View File

@@ -212,6 +212,12 @@ class Database
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// TODO: behavior without this attribute would be preferrable
// (actual types instead of all strings) but would be a breaking change
if ($this->type === 'sqlite') {
$this->connection->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
}
// store the connection
static::$connections[$this->id] = $this;

View File

@@ -45,7 +45,7 @@ class Db
// try to connect with the default
// connection settings if no params are set
$defaults = [
$params ??= [
'type' => Config::get('db.type', 'mysql'),
'host' => Config::get('db.host', 'localhost'),
'user' => Config::get('db.user', 'root'),
@@ -54,7 +54,6 @@ class Db
'prefix' => Config::get('db.prefix', ''),
'port' => Config::get('db.port', '')
];
$params = $params ?? $defaults;
return static::$connection = new Database($params);
}

View File

@@ -290,9 +290,10 @@ abstract class Sql
// add keys
foreach ($inner['keys'] as $key => $columns) {
// quote each column name and make a list string out of the column names
$columns = implode(', ', array_map(function ($name) {
return $this->quoteIdentifier($name);
}, $columns));
$columns = implode(', ', array_map(
fn ($name) => $this->quoteIdentifier($name),
$columns
));
if ($key === 'primary') {
$key = 'PRIMARY KEY';
@@ -526,7 +527,7 @@ abstract class Sql
];
}
$limit = $limit ?? '18446744073709551615';
$limit ??= '18446744073709551615';
$offsetBinding = $this->bindingName('offset');
$limitBinding = $this->bindingName('limit');

View File

@@ -82,9 +82,10 @@ class Sqlite extends Sql
$keys = [];
foreach ($inner['keys'] as $key => $columns) {
// quote each column name and make a list string out of the column names
$columns = implode(', ', array_map(function ($name) {
return $this->quoteIdentifier($name);
}, $columns));
$columns = implode(', ', array_map(
fn ($name) => $this->quoteIdentifier($name),
$columns
));
if ($key === 'primary') {
$inner['query'] .= ',' . PHP_EOL . 'PRIMARY KEY (' . $columns . ')';

View File

@@ -128,7 +128,7 @@ class Exception extends \Exception
]);
// handover to Exception parent class constructor
parent::__construct($message, null, $args['previous'] ?? null);
parent::__construct($message, 0, $args['previous'] ?? null);
}
// set the Exception code to the key

View File

@@ -5,6 +5,7 @@ namespace Kirby\Filesystem;
use Exception;
use Kirby\Cms\App;
use Kirby\Cms\Page;
use Kirby\Toolkit\Str;
use Throwable;
/**
@@ -450,7 +451,7 @@ class Dir
$modified = ($newModified > $modified) ? $newModified : $modified;
}
return $format !== null ? $handler($format, $modified) : $modified;
return Str::date($modified, $format, $handler);
}
/**
@@ -504,8 +505,8 @@ class Dir
}
// create the ignore pattern
$ignore = $ignore ?? static::$ignore;
$ignore = array_merge($ignore, ['.', '..']);
$ignore ??= static::$ignore;
$ignore = array_merge($ignore, ['.', '..']);
// scan for all files and dirs
$result = array_values((array)array_diff(scandir($dir), $ignore));

View File

@@ -475,11 +475,7 @@ class F
$modified = filemtime($file);
if (is_null($format) === true) {
return $modified;
}
return $handler($format, $modified);
return Str::date($modified, $format, $handler);
}
/**

View File

@@ -109,9 +109,10 @@ class Filename
'q' => $this->quality(),
];
$array = array_filter($array, function ($item) {
return $item !== null && $item !== false && $item !== '';
});
$array = array_filter(
$array,
fn ($item) => $item !== null && $item !== false && $item !== ''
);
return $array;
}

View File

@@ -320,7 +320,7 @@ class Mime
}
// get the extension or extract it from the filename
$extension = $extension ?? F::extension($file);
$extension ??= F::extension($file);
// try to guess the mime type at least
if ($mime === false) {

View File

@@ -71,8 +71,8 @@ class Field extends Component
$this->formFields = $formFields;
// use the type as fallback for the name
$attrs['name'] = $attrs['name'] ?? $type;
$attrs['type'] = $type;
$attrs['name'] ??= $type;
$attrs['type'] = $type;
parent::__construct($type, $attrs);
}
@@ -440,9 +440,10 @@ class Field extends Component
ksort($array);
return array_filter($array, function ($item) {
return $item !== null && is_object($item) === false;
});
return array_filter(
$array,
fn ($item) => $item !== null && is_object($item) === false
);
}
/**

View File

@@ -49,7 +49,7 @@ class BlocksField extends FieldClass
$type = $block['type'];
// get and cache fields at the same time
$fields[$type] = $fields[$type] ?? $this->fields($block['type']);
$fields[$type] ??= $this->fields($block['type']);
// overwrite the block content with form values
$block['content'] = $this->form($fields[$type], $block['content'])->$to();

View File

@@ -116,9 +116,10 @@ class LayoutField extends BlocksField
protected function setLayouts(array $layouts = [])
{
$this->layouts = array_map(function ($layout) {
return Str::split($layout);
}, $layouts);
$this->layouts = array_map(
fn ($layout) => Str::split($layout),
$layouts
);
}
protected function setSettings($settings = null)

View File

@@ -726,9 +726,7 @@ abstract class FieldClass
ksort($props);
return array_filter($props, function ($item) {
return $item !== null;
});
return array_filter($props, fn ($item) => $item !== null);
}
/**

View File

@@ -29,7 +29,7 @@ class Fields extends Collection
{
if (is_array($field) === true) {
// use the array key as name if the name is not set
$field['name'] = $field['name'] ?? $name;
$field['name'] ??= $name;
$field = Field::factory($field['type'], $field, $this);
}

View File

@@ -268,9 +268,9 @@ class Form
}
// set a few defaults
$props['values'] = array_merge($original, $values);
$props['fields'] = $props['fields'] ?? [];
$props['model'] = $model;
$props['values'] = array_merge($original, $values);
$props['fields'] ??= [];
$props['model'] = $model;
// search for the blueprint
if (method_exists($model, 'blueprint') === true && $blueprint = $model->blueprint()) {

View File

@@ -45,10 +45,10 @@ class Options
*/
public static function api($api, $model = null): array
{
$model = $model ?? App::instance()->site();
$fetch = null;
$text = null;
$value = null;
$model ??= App::instance()->site();
$fetch = null;
$text = null;
$value = null;
if (is_array($api) === true) {
$fetch = $api['fetch'] ?? null;
@@ -165,7 +165,7 @@ class Options
*/
public static function query($query, $model = null): array
{
$model = $model ?? App::instance()->site();
$model ??= App::instance()->site();
// default text setup
$text = [

View File

@@ -3,7 +3,6 @@
namespace Kirby\Http;
use Kirby\Toolkit\Str;
use TrueBV\Punycode;
/**
* Handles Internationalized Domain Names
@@ -16,14 +15,26 @@ use TrueBV\Punycode;
*/
class Idn
{
/**
* Convert domain name from IDNA ASCII to Unicode
*
* @param string $domain
* @return string|false
*/
public static function decode(string $domain)
{
return (new Punycode())->decode($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 (new Punycode())->encode($domain);
return idn_to_ascii($domain);
}
/**

View File

@@ -43,7 +43,7 @@ class Query extends Obj
public function toString($questionMark = false): string
{
$query = http_build_query($this, null, '&', PHP_QUERY_RFC3986);
$query = http_build_query($this, '', '&', PHP_QUERY_RFC3986);
if (empty($query) === true) {
return '';

View File

@@ -174,7 +174,7 @@ class Request
*/
public function body()
{
return $this->body = $this->body ?? new Body();
return $this->body ??= new Body();
}
/**
@@ -220,7 +220,7 @@ class Request
$methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'];
// the request method can be overwritten with a header
$methodOverride = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ?? null);
$methodOverride = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ?? '');
if ($method === null && in_array($methodOverride, $methods) === true) {
$method = $methodOverride;
@@ -269,7 +269,7 @@ class Request
*/
public function files()
{
return $this->files = $this->files ?? new Files();
return $this->files ??= new Files();
}
/**
@@ -379,7 +379,7 @@ class Request
*/
public function query()
{
return $this->query = $this->query ?? new Query();
return $this->query ??= new Query();
}
/**
@@ -407,6 +407,6 @@ class Request
return $this->url()->clone($props);
}
return $this->url = $this->url ?? Uri::current();
return $this->url ??= Uri::current();
}
}

View File

@@ -160,14 +160,14 @@ class Response
throw new Exception('The file could not be found');
}
$filename = $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' => 'application/force-download',
'type' => F::mime($file),
'headers' => [
'Pragma' => 'public',
'Cache-Control' => 'no-cache, no-store, must-revalidate',
@@ -234,7 +234,7 @@ class Response
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 : null);
$body = json_encode($body, $pretty === true ? JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES : 0);
}
return new static([

View File

@@ -5,6 +5,7 @@ namespace Kirby\Http;
use Closure;
use Exception;
use InvalidArgumentException;
use Kirby\Toolkit\A;
/**
* @package Kirby Http
@@ -59,8 +60,11 @@ class Router
throw new InvalidArgumentException('Invalid route parameters');
}
$methods = array_map('trim', explode('|', strtoupper($props['method'] ?? 'GET')));
$patterns = is_array($props['pattern']) === false ? [$props['pattern']] : $props['pattern'];
$patterns = A::wrap($props['pattern']);
$methods = A::map(
explode('|', strtoupper($props['method'] ?? 'GET')),
'trim'
);
if ($methods === ['ALL']) {
$methods = array_keys($this->routes);
@@ -88,7 +92,7 @@ class Router
*/
public function call(string $path = null, string $method = 'GET', Closure $callback = null)
{
$path = $path ?? '';
$path ??= '';
$ignore = [];
$result = null;
$loop = true;

View File

@@ -92,6 +92,9 @@ class Server
*/
public static function sanitize(string $key, $value)
{
// make sure $value is not null
$value ??= '';
switch ($key) {
case 'SERVER_ADDR':
case 'SERVER_NAME':
@@ -100,7 +103,7 @@ class Server
$value = strip_tags($value);
$value = preg_replace('![^\w.:-]+!iu', '', $value);
$value = trim($value, '-');
$value = htmlspecialchars($value);
$value = htmlspecialchars($value, ENT_COMPAT);
break;
case 'SERVER_PORT':
case 'HTTP_X_FORWARDED_PORT':

View File

@@ -144,10 +144,10 @@ class Uri
// parse the path and extract params
if (empty($props['path']) === false) {
$extract = Params::extract($props['path']);
$props['params'] = $props['params'] ?? $extract['params'];
$props['path'] = $extract['path'];
$props['slash'] = $props['slash'] ?? $extract['slash'];
$extract = Params::extract($props['path']);
$props['params'] ??= $extract['params'];
$props['path'] = $extract['path'];
$props['slash'] ??= $extract['slash'];
}
$this->setProperties($this->props = $props);
@@ -246,8 +246,12 @@ class Uri
return static::$current;
}
$uri = Server::get('REQUEST_URI');
$uri = preg_replace('!^(http|https)\:\/\/' . Server::get('HTTP_HOST') . '!', '', $uri);
$uri = Server::get('REQUEST_URI') ?? '';
$uri = preg_replace(
'!^(http|https)\:\/\/' . Server::get('HTTP_HOST') . '!',
'',
$uri
);
$uri = parse_url('http://getkirby.com' . $uri);
$url = new static(array_merge([

View File

@@ -145,8 +145,8 @@ class Url
}
// build the full url
$path = ltrim($path, '/');
$home = $home ?? static::home();
$path = ltrim($path, '/');
$home ??= static::home();
if (empty($path) === true) {
return $home;
@@ -260,6 +260,9 @@ class Url
*/
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;

View File

@@ -177,7 +177,7 @@ class ImageMagick extends Darkroom
{
// simple resize
if ($options['crop'] === false) {
return sprintf('-resize %sx%s!', $options['width'], $options['height']);
return sprintf('-thumbnail %sx%s!', $options['width'], $options['height']);
}
$gravities = [
@@ -195,7 +195,7 @@ class ImageMagick extends Darkroom
// translate the gravity option into something imagemagick understands
$gravity = $gravities[$options['crop']] ?? 'Center';
$command = sprintf('-resize %sx%s^', $options['width'], $options['height']);
$command = sprintf('-thumbnail %sx%s^', $options['width'], $options['height']);
$command .= sprintf(' -gravity %s -crop %sx%s+0+0', $gravity, $options['width'], $options['height']);
return $command;
@@ -227,6 +227,14 @@ class ImageMagick extends Darkroom
*/
protected function strip(string $file, array $options): string
{
return '-strip';
if (F::extension($file) === 'png') {
// ImageMagick does not support keeping ICC profiles while
// stripping other privacy- and security-related information,
// such as GPS data; so discard all color profiles for PNG files
// (tested with ImageMagick 7.0.11-14 Q16 x86_64 2021-05-31)
return '-strip';
}
return '';
}
}

View File

@@ -116,7 +116,7 @@ class Image extends File
*/
public function exif()
{
return $this->exif = $this->exif ?? new Exif($this);
return $this->exif ??= new Exif($this);
}
/**

View File

@@ -130,9 +130,10 @@ class Document
// remove missing files
$assets['css'] = array_filter($assets['css']);
$assets['js'] = array_filter($assets['js'], function ($js) {
return empty($js['src']) === false;
});
$assets['js'] = array_filter(
$assets['js'],
fn ($js) => empty($js['src']) === false
);
return $assets;
}

View File

@@ -316,7 +316,7 @@ class File extends Model
$absolute = $parent !== $params['model'];
}
$params['text'] = $params['text'] ?? '{{ file.filename }}';
$params['text'] ??= '{{ file.filename }}';
return array_merge(parent::pickerData($params), [
'filename' => $name,

View File

@@ -68,7 +68,7 @@ abstract class Json
}
// always inject the response code
$data['code'] = $data['code'] ?? 200;
$data['code'] ??= 200;
$data['path'] = $options['path'] ?? null;
$data['referrer'] = Panel::referrer();

View File

@@ -77,7 +77,7 @@ abstract class Model
*/
public function dragTextType(string $type = null): string
{
$type = $type ?? 'auto';
$type ??= 'auto';
if ($type === 'auto') {
$type = option('panel.kirbytext', true) ? 'kirbytext' : 'markdown';
@@ -141,8 +141,8 @@ abstract class Model
// main url
$settings['url'] = $image->url();
// only create srcsets for actual File objects
if (is_a($image, 'Kirby\Cms\File') === true) {
// only create srcsets for resizable files
if ($image->isResizable() === true) {
$settings['src'] = static::imagePlaceholder();
switch ($layout) {
@@ -174,6 +174,8 @@ abstract class Model
]
]);
}
} elseif ($image->isViewable() === true) {
$settings['src'] = $image->url();
}
}

View File

@@ -231,7 +231,7 @@ class Page extends Model
*/
public function pickerData(array $params = []): array
{
$params['text'] = $params['text'] ?? '{{ page.title }}';
$params['text'] ??= '{{ page.title }}';
return array_merge(parent::pickerData($params), [
'dragText' => $this->dragText(),

View File

@@ -36,14 +36,14 @@ class Panel
*/
public static function area(string $id, $area): array
{
$area['id'] = $id;
$area['label'] = $area['label'] ?? $id;
$area['breadcrumb'] = $area['breadcrumb'] ?? [];
$area['breadcrumbLabel'] = $area['breadcrumbLabel'] ?? $area['label'];
$area['title'] = $area['label'];
$area['menu'] = $area['menu'] ?? false;
$area['link'] = $area['link'] ?? $id;
$area['search'] = $area['search'] ?? null;
$area['id'] = $id;
$area['label'] ??= $id;
$area['breadcrumb'] ??= [];
$area['breadcrumbLabel'] ??= $area['label'];
$area['title'] = $area['label'];
$area['menu'] ??= false;
$area['link'] ??= $id;
$area['search'] ??= null;
return $area;
}
@@ -229,11 +229,14 @@ class Panel
/**
* Returns the referrer path if present
*
* @return string|null
* @return string
*/
public static function referrer(): ?string
public static function referrer(): string
{
$referrer = kirby()->request()->header('X-Fiber-Referrer') ?? get('_referrer');
$referrer = kirby()->request()->header('X-Fiber-Referrer')
?? get('_referrer')
?? '';
return '/' . trim($referrer, '/');
}
@@ -302,9 +305,6 @@ class Panel
// create a micro-router for the Panel
return router($path, $method = $kirby->request()->method(), $routes, function ($route) use ($areas, $kirby, $method, $path) {
// trigger hook
$route = $kirby->apply('panel.route:before', compact('route', 'path', 'method'), 'route');
// route needs authentication?
$auth = $route->attributes()['auth'] ?? true;
$areaId = $route->attributes()['area'] ?? null;
@@ -313,6 +313,9 @@ class Panel
// call the route action to check the result
try {
// trigger hook
$route = $kirby->apply('panel.route:before', compact('route', 'path', 'method'), 'route');
// check for access before executing area routes
if ($auth !== false) {
static::firewall($kirby->user(), $areaId);
@@ -450,6 +453,15 @@ class Panel
$routes = [];
foreach ($dropdowns as $name => $dropdown) {
// Handle shortcuts for dropdowns. The name is the pattern
// and options are defined in a Closure
if (is_a($dropdown, 'Closure') === true) {
$dropdown = [
'pattern' => $name,
'action' => $dropdown
];
}
// create the full pattern with dropdowns prefix
$pattern = 'dropdowns/' . trim(($dropdown['pattern'] ?? $name), '/');

View File

@@ -172,7 +172,7 @@ class User extends Model
*/
public function pickerData(array $params = null): array
{
$params['text'] = $params['text'] ?? '{{ user.username }}';
$params['text'] ??= '{{ user.username }}';
return array_merge(parent::pickerData($params), [
'email' => $this->model->email(),

View File

@@ -47,22 +47,25 @@ class Svg extends Xml
* @var array
*/
public static $allowedAttrs = [
'accent-height',
'accumulate',
'additive',
'alignment-baseline',
'ascent',
'attributeName',
'attributeType',
'azimuth',
'baseFrequency',
'baseline-shift',
'begin',
'bias',
'by',
// core attributes
'id',
'lang',
'tabindex',
'xml:id',
'xml:lang',
'xml:space',
// styling attributes
'class',
'style',
// conditional processing attributes
'systemLanguage',
// presentation attributes
'alignment-baseline',
'baseline-shift',
'clip',
'clipPathUnits',
'clip-path',
'clip-rule',
'color',
@@ -70,24 +73,15 @@ class Svg extends Xml
'color-interpolation-filters',
'color-profile',
'color-rendering',
'cx',
'cy',
'd',
'dx',
'dy',
'diffuseConstant',
'direction',
'display',
'divisor',
'dur',
'edgeMode',
'elevation',
'end',
'dominant-baseline',
'enable-background',
'fill',
'fill-opacity',
'fill-rule',
'filter',
'filterUnits',
'flood-color',
'flood-opacity',
'font-family',
@@ -97,18 +91,103 @@ class Svg extends Xml
'font-style',
'font-variant',
'font-weight',
'image-rendering',
'kerning',
'letter-spacing',
'lighting-color',
'marker-end',
'marker-mid',
'marker-start',
'mask',
'opacity',
'overflow',
'paint-order',
'shape-rendering',
'stop-color',
'stop-opacity',
'stroke',
'stroke-dasharray',
'stroke-dashoffset',
'stroke-linecap',
'stroke-linejoin',
'stroke-miterlimit',
'stroke-opacity',
'stroke-width',
'text-anchor',
'text-decoration',
'text-rendering',
'transform',
'visibility',
'word-spacing',
'writing-mode',
// animation attribute target attributes
'attributeName',
'attributeType',
// animation timing attributes
'begin',
'dur',
'end',
'max',
'min',
'repeatCount',
'repeatDur',
'restart',
// animation value attributes
'by',
'from',
'keySplines',
'keyTimes',
'to',
'values',
// animation addition attributes
'accumulate',
'additive',
// filter primitive attributes
'height',
'result',
'width',
'x',
'y',
// transfer function attributes
'amplitude',
'exponent',
'intercept',
'offset',
'slope',
'tableValues',
'type',
// other attributes specific to one or multiple elements
'azimuth',
'baseFrequency',
'bias',
'clipPathUnits',
'cx',
'cy',
'diffuseConstant',
'divisor',
'dx',
'dy',
'edgeMode',
'elevation',
'filterUnits',
'fr',
'fx',
'fy',
'g1',
'g2',
'glyph-name',
'glyphRef',
'gradientUnits',
'gradientTransform',
'height',
'gradientUnits',
'href',
'id',
'image-rendering',
'hreflang',
'in',
'in2',
'k',
@@ -116,116 +195,73 @@ class Svg extends Xml
'k2',
'k3',
'k4',
'kerning',
'keyPoints',
'keySplines',
'keyTimes',
'lang',
'lengthAdjust',
'letter-spacing',
'kernelMatrix',
'kernelUnitLength',
'lighting-color',
'local',
'marker-end',
'marker-mid',
'marker-start',
'keyPoints',
'lengthAdjust',
'limitingConeAngle',
'markerHeight',
'markerUnits',
'markerWidth',
'maskContentUnits',
'maskUnits',
'max',
'mask',
'media',
'method',
'mode',
'min',
'name',
'numOctaves',
'offset',
'operator',
'opacity',
'order',
'orient',
'orientation',
'origin',
'overflow',
'paint-order',
'path',
'pathLength',
'patternContentUnits',
'patternTransform',
'patternUnits',
'points',
'pointsAtX',
'pointsAtY',
'pointsAtZ',
'preserveAlpha',
'preserveAspectRatio',
'primitiveUnits',
'r',
'rx',
'ry',
'radius',
'refX',
'refY',
'repeatCount',
'repeatDur',
'restart',
'result',
'rotate',
'rx',
'ry',
'scale',
'seed',
'shape-rendering',
'side',
'spacing',
'specularConstant',
'specularExponent',
'spreadMethod',
'startOffset',
'stdDeviation',
'stitchTiles',
'stop-color',
'stop-opacity',
'stroke-dasharray',
'stroke-dashoffset',
'stroke-linecap',
'stroke-linejoin',
'stroke-miterlimit',
'stroke-opacity',
'stroke',
'stroke-width',
'style',
'surfaceScale',
'systemLanguage',
'tabindex',
'targetX',
'targetY',
'transform',
'text-anchor',
'text-decoration',
'text-rendering',
'textLength',
'type',
'u1',
'u2',
'unicode',
'values',
'viewBox',
'visibility',
'version',
'vert-adv-y',
'vert-origin-x',
'vert-origin-y',
'width',
'word-spacing',
'wrap',
'writing-mode',
'xChannelSelector',
'yChannelSelector',
'x',
'viewBox',
'x1',
'x2',
'xChannelSelector',
'xlink:href',
'y',
'xlink:title',
'y1',
'y2',
'yChannelSelector',
'z',
'zoomAndPan',
];
@@ -247,12 +283,9 @@ class Svg extends Xml
* `allowedAttrs` list or `false` to allow the tag without
* any attributes
*
* @todo Move attributes from the global list to their tags
*
* @var array
*/
public static $allowedTags = [
'svg' => true,
'a' => true,
'altGlyph' => true,
'altGlyphDef' => true,
@@ -265,39 +298,6 @@ class Svg extends Xml
'defs' => true,
'desc' => true,
'ellipse' => true,
'filter' => true,
'font' => true,
'g' => true,
'glyph' => true,
'glyphRef' => true,
'hkern' => true,
'image' => true,
'line' => true,
'linearGradient' => true,
'marker' => true,
'mask' => true,
'metadata' => true,
'mpath' => true,
'path' => true,
'pattern' => true,
'polygon' => true,
'polyline' => true,
'radialGradient' => true,
'rect' => true,
'stop' => true,
'style' => true,
'switch' => true,
'symbol' => true,
'text' => true,
'textPath' => true,
'title' => true,
'tref' => true,
'tspan' => true,
'use' => true,
'view' => true,
'vkern' => true,
// filters
'feBlend' => true,
'feColorMatrix' => true,
'feComponentTransfer' => true,
@@ -321,6 +321,38 @@ class Svg extends Xml
'feSpotLight' => true,
'feTile' => true,
'feTurbulence' => true,
'filter' => true,
'font' => true,
'g' => true,
'glyph' => true,
'glyphRef' => true,
'hkern' => true,
'image' => true,
'line' => true,
'linearGradient' => true,
'marker' => true,
'mask' => true,
'metadata' => true,
'mpath' => true,
'path' => true,
'pattern' => true,
'polygon' => true,
'polyline' => true,
'radialGradient' => true,
'rect' => true,
'stop' => true,
'style' => true,
'svg' => true,
'switch' => true,
'symbol' => true,
'text' => true,
'textPath' => true,
'title' => true,
'tref' => true,
'tspan' => true,
'use' => true,
'view' => true,
'vkern' => true,
];
/**

View File

@@ -467,10 +467,7 @@ class A
{
// convert a simple ignore list to a nested $key => true array
if (isset($ignore[0]) === true) {
$ignore = array_map(function () {
return true;
}, array_flip($ignore));
$ignore = array_map(fn () => true, array_flip($ignore));
$ignore = A::nest($ignore);
}

View File

@@ -138,7 +138,7 @@ class Dom
*/
public function body()
{
return $this->body = $this->body ?? $this->query('/html/body')[0] ?? null;
return $this->body ??= $this->query('/html/body')[0] ?? null;
}
/**
@@ -455,11 +455,20 @@ class Dom
};
}
if ($allowedNamespaces === true) {
// take the list as it is and only consider
// if the configuration does not define namespace URIs or if the
// currently checked node is from the special `xml:` namespace
// that has a fixed namespace according to the XML spec...
if ($allowedNamespaces === true || $node->namespaceURI === 'http://www.w3.org/XML/1998/namespace') {
// ...take the list as it is and only consider
// exact matches of the local name (which will
// contain a namespace if that namespace name
// is not defined in the document)
// the list contains the `xml:` prefix, so add it to the name as well
if ($node->namespaceURI === 'http://www.w3.org/XML/1998/namespace') {
$localName = 'xml:' . $localName;
}
foreach ($list as $item) {
if ($compare($item, $localName) === true) {
return $item;

View File

@@ -81,7 +81,7 @@ class Escape
*/
protected static function escaper()
{
return static::$escaper = static::$escaper ?? new Escaper('utf-8');
return static::$escaper ??= new Escaper('utf-8');
}
/**

View File

@@ -24,6 +24,34 @@ class Html extends Xml
*/
public static $entities;
/**
* List of HTML tags that can be used inline
*
* @var array
*/
public static $inlineList = [
'b',
'i',
'small',
'abbr',
'cite',
'code',
'dfn',
'em',
'kbd',
'strong',
'samp',
'var',
'a',
'bdo',
'br',
'img',
'q',
'span',
'sub',
'sup'
];
/**
* Closing string for void tags;
* can be used to switch to trailing slashes if required
@@ -117,6 +145,10 @@ class Html extends Xml
// all other cases can share the XML variant
$attr = parent::attr($name, $value);
if ($attr === null) {
return null;
}
// HTML supports named entities
$entities = parent::entities();
$html = array_keys($entities);
@@ -205,7 +237,7 @@ class Html extends Xml
*/
public static function entities(): array
{
return self::$entities = self::$entities ?? get_html_translation_table(HTML_ENTITIES);
return self::$entities ??= get_html_translation_table(HTML_ENTITIES);
}
/**
@@ -325,7 +357,7 @@ class Html extends Xml
*/
public static function rel(?string $rel = null, ?string $target = null): ?string
{
$rel = trim($rel);
$rel = trim($rel ?? '');
if ($target === '_blank') {
if (empty($rel) === false) {

View File

@@ -115,7 +115,7 @@ class I18n
*/
public static function formatNumber($number, string $locale = null): string
{
$locale = $locale ?? static::locale();
$locale ??= static::locale();
$formatter = static::decimalNumberFormatter($locale);
if ($formatter !== null) {
@@ -154,7 +154,7 @@ class I18n
*/
public static function translate($key, $fallback = null, string $locale = null)
{
$locale = $locale ?? static::locale();
$locale ??= static::locale();
if (is_array($key) === true) {
if (isset($key[$locale])) {
@@ -224,7 +224,7 @@ class I18n
*/
public static function translation(string $locale = null): array
{
$locale = $locale ?? static::locale();
$locale ??= static::locale();
if (isset(static::$translations[$locale]) === true) {
return static::$translations[$locale];
@@ -283,8 +283,7 @@ class I18n
*/
public static function translateCount(string $key, int $count, string $locale = null, bool $formatNumber = true)
{
$locale = $locale ?? static::locale();
$locale ??= static::locale();
$translation = static::translate($key, null, $locale);
if ($translation === null) {

View File

@@ -40,7 +40,7 @@ class Iterator implements IteratorAggregate
*
* @return \ArrayIterator
*/
public function getIterator()
public function getIterator(): ArrayIterator
{
return new ArrayIterator($this->data);
}

View File

@@ -115,7 +115,7 @@ trait Properties
}
// fetch the default value from the property
$value = $value ?? $this->$name ?? null;
$value ??= $this->$name ?? null;
// store all original properties, to be able to clone them later
$this->propertyData[$name] = $value;

View File

@@ -3,6 +3,7 @@
namespace Kirby\Toolkit;
use Exception;
use Kirby\Exception\InvalidArgumentException;
/**
* The String class provides a set
@@ -180,9 +181,9 @@ class Str
if ($position === false) {
return '';
} else {
return static::substr($string, $position + static::length($needle));
}
return static::substr($string, $position + static::length($needle));
}
/**
@@ -222,9 +223,9 @@ class Str
if ($position === false) {
return '';
} else {
return static::substr($string, 0, $position);
}
return static::substr($string, 0, $position);
}
/**
@@ -250,7 +251,40 @@ class Str
*/
public static function contains(string $string = null, string $needle, bool $caseInsensitive = false): bool
{
return call_user_func($caseInsensitive === true ? 'stripos' : 'strpos', $string, $needle) !== false;
if ($needle === '') {
return true;
}
$method = $caseInsensitive === true ? 'stripos' : 'strpos';
return call_user_func($method, $string, $needle) !== false;
}
/**
* Convert timestamp to date string
* according to locale settings
*
* @param int|null $time
* @param string|null $format
* @param string $handler date or strftime
* @return string|int
*/
public static function date(?int $time = null, ?string $format = null, string $handler = 'date')
{
if (is_null($format) === true) {
return $time;
}
// separately handle strftime to be able
// to suppress deprecation warning
// TODO: remove strftime support for PHP 9.0
if ($handler === 'strftime') {
// make sure timezone is set correctly
date_default_timezone_get();
return @strftime($format, $time);
}
return $handler($format, $time);
}
/**
@@ -373,6 +407,9 @@ class Str
*/
public static function float($value): string
{
// make sure $value is not null
$value ??= '';
// Convert exponential to decimal, 1e-8 as 0.00000001
if (strpos(strtolower($value), 'e') !== false) {
$value = rtrim(sprintf('%.16f', (float)$value), '0');
@@ -397,9 +434,9 @@ class Str
if ($position === false) {
return '';
} else {
return static::substr($string, $position);
}
return static::substr($string, $position);
}
/**
@@ -436,7 +473,7 @@ class Str
*/
public static function length(string $string = null): int
{
return mb_strlen($string, 'UTF-8');
return mb_strlen($string ?? '', 'UTF-8');
}
/**
@@ -512,6 +549,10 @@ class Str
*/
public static function position(string $string = null, string $needle, bool $caseInsensitive = false)
{
if ($needle === '') {
throw new InvalidArgumentException('The needle must not be empty');
}
if ($caseInsensitive === true) {
$string = static::lower($string);
$needle = static::lower($needle);
@@ -899,10 +940,10 @@ class Str
*/
public static function slug(string $string = null, string $separator = null, string $allowed = null, int $maxlength = 128): string
{
$separator = $separator ?? static::$defaults['slug']['separator'];
$allowed = $allowed ?? static::$defaults['slug']['allowed'];
$separator ??= static::$defaults['slug']['separator'];
$allowed ??= static::$defaults['slug']['allowed'];
$string = trim($string);
$string = trim($string ?? '');
$string = static::lower($string);
$string = static::ascii($string);
@@ -958,8 +999,11 @@ class Str
return $string;
}
$parts = explode($separator, $string);
$out = [];
// make sure $string is string
$string ??= '';
$parts = explode($separator, $string);
$out = [];
foreach ($parts as $p) {
$p = trim($p);
@@ -1046,6 +1090,9 @@ class Str
$start = (string)($options['start'] ?? $start);
$end = (string)($options['end'] ?? $end);
// make sure $string is string
$string ??= '';
return preg_replace_callback('!' . $start . '(.*?)' . $end . '!', function ($match) use ($data, $fallback, $callback) {
$query = trim($match[1]);
@@ -1084,8 +1131,12 @@ class Str
*/
public static function toBytes($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] ?? null);
$last = strtolower($size[strlen($size)-1] ?? '');
$size = (int)$size;
switch ($last) {
@@ -1198,9 +1249,9 @@ class Str
if ($position === false) {
return '';
} else {
return static::substr($string, 0, $position + static::length($needle));
}
return static::substr($string, 0, $position + static::length($needle));
}
/**
@@ -1224,6 +1275,9 @@ class Str
*/
public static function widont(string $string = null): string
{
// make sure $string is string
$string ??= '';
// Replace space between last word and punctuation
$string = preg_replace_callback('|(\S)\s(\S?)$|u', function ($matches) {
return $matches[1] . '&nbsp;' . $matches[2];

View File

@@ -249,6 +249,9 @@ V::$validators = [
* third argument to compare them.
*/
'date' => function (?string $value, string $operator = null, string $test = null): bool {
// make sure $value is a string
$value ??= '';
$args = func_get_args();
// simple date validation
@@ -522,6 +525,6 @@ V::$validators = [
// In search for the perfect regular expression: https://mathiasbynens.be/demo/url-regex
// 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;
return preg_match($regex, $value ?? '') !== 0;
}
];

View File

@@ -106,9 +106,10 @@ class Xml
if (isset($value['value'], $value['escape'])) {
$value = $value['escape'] === true ? static::encode($value['value']) : $value['value'];
} else {
$value = implode(' ', array_filter($value, function ($value) {
return !empty($value) || is_numeric($value);
}));
$value = implode(' ', array_filter(
$value,
fn ($value) => !empty($value) || is_numeric($value)
));
}
} else {
$value = static::encode($value);
@@ -420,7 +421,8 @@ class Xml
return $value;
}
$encoded = htmlentities($value);
// TODO: in 3.7.0 use ENT_NOQUOTES | ENT_XML1 instead
$encoded = htmlentities($value, ENT_COMPAT);
if ($encoded === $value) {
// no CDATA block needed
return $value;