Upgrade to 3.7.1
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -19,278 +19,278 @@ use TypeError;
|
||||
*/
|
||||
class Component
|
||||
{
|
||||
/**
|
||||
* Registry for all component mixins
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $mixins = [];
|
||||
/**
|
||||
* Registry for all component mixins
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $mixins = [];
|
||||
|
||||
/**
|
||||
* Registry for all component types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $types = [];
|
||||
/**
|
||||
* Registry for all component types
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $types = [];
|
||||
|
||||
/**
|
||||
* An array of all passed attributes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attrs = [];
|
||||
/**
|
||||
* An array of all passed attributes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $attrs = [];
|
||||
|
||||
/**
|
||||
* An array of all computed properties
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $computed = [];
|
||||
/**
|
||||
* An array of all computed properties
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $computed = [];
|
||||
|
||||
/**
|
||||
* An array of all registered methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $methods = [];
|
||||
/**
|
||||
* An array of all registered methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $methods = [];
|
||||
|
||||
/**
|
||||
* An array of all component options
|
||||
* from the component definition
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
/**
|
||||
* An array of all component options
|
||||
* from the component definition
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* An array of all resolved props
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $props = [];
|
||||
/**
|
||||
* An array of all resolved props
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $props = [];
|
||||
|
||||
/**
|
||||
* The component type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
/**
|
||||
* The component type
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Magic caller for defined methods and properties
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $name, array $arguments = [])
|
||||
{
|
||||
if (array_key_exists($name, $this->computed) === true) {
|
||||
return $this->computed[$name];
|
||||
}
|
||||
/**
|
||||
* Magic caller for defined methods and properties
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $name, array $arguments = [])
|
||||
{
|
||||
if (array_key_exists($name, $this->computed) === true) {
|
||||
return $this->computed[$name];
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $this->props) === true) {
|
||||
return $this->props[$name];
|
||||
}
|
||||
if (array_key_exists($name, $this->props) === true) {
|
||||
return $this->props[$name];
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $this->methods) === true) {
|
||||
return $this->methods[$name]->call($this, ...$arguments);
|
||||
}
|
||||
if (array_key_exists($name, $this->methods) === true) {
|
||||
return $this->methods[$name]->call($this, ...$arguments);
|
||||
}
|
||||
|
||||
return $this->$name;
|
||||
}
|
||||
return $this->$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new component for the given type
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $attrs
|
||||
*/
|
||||
public function __construct(string $type, array $attrs = [])
|
||||
{
|
||||
if (isset(static::$types[$type]) === false) {
|
||||
throw new InvalidArgumentException('Undefined component type: ' . $type);
|
||||
}
|
||||
/**
|
||||
* Creates a new component for the given type
|
||||
*
|
||||
* @param string $type
|
||||
* @param array $attrs
|
||||
*/
|
||||
public function __construct(string $type, array $attrs = [])
|
||||
{
|
||||
if (isset(static::$types[$type]) === false) {
|
||||
throw new InvalidArgumentException('Undefined component type: ' . $type);
|
||||
}
|
||||
|
||||
$this->attrs = $attrs;
|
||||
$this->options = $options = $this->setup($type);
|
||||
$this->methods = $methods = $options['methods'] ?? [];
|
||||
$this->attrs = $attrs;
|
||||
$this->options = $options = $this->setup($type);
|
||||
$this->methods = $methods = $options['methods'] ?? [];
|
||||
|
||||
foreach ($attrs as $attrName => $attrValue) {
|
||||
$this->$attrName = $attrValue;
|
||||
}
|
||||
foreach ($attrs as $attrName => $attrValue) {
|
||||
$this->$attrName = $attrValue;
|
||||
}
|
||||
|
||||
if (isset($options['props']) === true) {
|
||||
$this->applyProps($options['props']);
|
||||
}
|
||||
if (isset($options['props']) === true) {
|
||||
$this->applyProps($options['props']);
|
||||
}
|
||||
|
||||
if (isset($options['computed']) === true) {
|
||||
$this->applyComputed($options['computed']);
|
||||
}
|
||||
if (isset($options['computed']) === true) {
|
||||
$this->applyComputed($options['computed']);
|
||||
}
|
||||
|
||||
$this->attrs = $attrs;
|
||||
$this->methods = $methods;
|
||||
$this->options = $options;
|
||||
$this->type = $type;
|
||||
}
|
||||
$this->attrs = $attrs;
|
||||
$this->methods = $methods;
|
||||
$this->options = $options;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback for missing properties to return
|
||||
* null instead of an error
|
||||
*
|
||||
* @param string $attr
|
||||
* @return null
|
||||
*/
|
||||
public function __get(string $attr)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Fallback for missing properties to return
|
||||
* null instead of an error
|
||||
*
|
||||
* @param string $attr
|
||||
* @return null
|
||||
*/
|
||||
public function __get(string $attr)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of default options for each component.
|
||||
* This can be overwritten by extended classes
|
||||
* to define basic options that should always
|
||||
* be applied.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function defaults(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* A set of default options for each component.
|
||||
* This can be overwritten by extended classes
|
||||
* to define basic options that should always
|
||||
* be applied.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function defaults(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all defined props and apply the
|
||||
* passed values.
|
||||
*
|
||||
* @param array $props
|
||||
* @return void
|
||||
*/
|
||||
protected function applyProps(array $props): void
|
||||
{
|
||||
foreach ($props as $propName => $propFunction) {
|
||||
if (is_a($propFunction, 'Closure') === true) {
|
||||
if (isset($this->attrs[$propName]) === true) {
|
||||
try {
|
||||
$this->$propName = $this->props[$propName] = $propFunction->call($this, $this->attrs[$propName]);
|
||||
} catch (TypeError $e) {
|
||||
throw new TypeError('Invalid value for "' . $propName . '"');
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$this->$propName = $this->props[$propName] = $propFunction->call($this);
|
||||
} catch (ArgumentCountError $e) {
|
||||
throw new ArgumentCountError('Please provide a value for "' . $propName . '"');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->$propName = $this->props[$propName] = $propFunction;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Register all defined props and apply the
|
||||
* passed values.
|
||||
*
|
||||
* @param array $props
|
||||
* @return void
|
||||
*/
|
||||
protected function applyProps(array $props): void
|
||||
{
|
||||
foreach ($props as $propName => $propFunction) {
|
||||
if (is_a($propFunction, 'Closure') === true) {
|
||||
if (isset($this->attrs[$propName]) === true) {
|
||||
try {
|
||||
$this->$propName = $this->props[$propName] = $propFunction->call($this, $this->attrs[$propName]);
|
||||
} catch (TypeError $e) {
|
||||
throw new TypeError('Invalid value for "' . $propName . '"');
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$this->$propName = $this->props[$propName] = $propFunction->call($this);
|
||||
} catch (ArgumentCountError $e) {
|
||||
throw new ArgumentCountError('Please provide a value for "' . $propName . '"');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->$propName = $this->props[$propName] = $propFunction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all computed properties and calculate their values.
|
||||
* This must happen after all props are registered.
|
||||
*
|
||||
* @param array $computed
|
||||
* @return void
|
||||
*/
|
||||
protected function applyComputed(array $computed): void
|
||||
{
|
||||
foreach ($computed as $computedName => $computedFunction) {
|
||||
if (is_a($computedFunction, 'Closure') === true) {
|
||||
$this->$computedName = $this->computed[$computedName] = $computedFunction->call($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Register all computed properties and calculate their values.
|
||||
* This must happen after all props are registered.
|
||||
*
|
||||
* @param array $computed
|
||||
* @return void
|
||||
*/
|
||||
protected function applyComputed(array $computed): void
|
||||
{
|
||||
foreach ($computed as $computedName => $computedFunction) {
|
||||
if (is_a($computedFunction, 'Closure') === true) {
|
||||
$this->$computedName = $this->computed[$computedName] = $computedFunction->call($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a component definition by type
|
||||
*
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public static function load(string $type): array
|
||||
{
|
||||
$definition = static::$types[$type];
|
||||
/**
|
||||
* Load a component definition by type
|
||||
*
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public static function load(string $type): array
|
||||
{
|
||||
$definition = static::$types[$type];
|
||||
|
||||
// load definitions from string
|
||||
if (is_string($definition) === true) {
|
||||
if (is_file($definition) !== true) {
|
||||
throw new Exception('Component definition ' . $definition . ' does not exist');
|
||||
}
|
||||
// load definitions from string
|
||||
if (is_string($definition) === true) {
|
||||
if (is_file($definition) !== true) {
|
||||
throw new Exception('Component definition ' . $definition . ' does not exist');
|
||||
}
|
||||
|
||||
static::$types[$type] = $definition = F::load($definition);
|
||||
}
|
||||
static::$types[$type] = $definition = F::load($definition);
|
||||
}
|
||||
|
||||
return $definition;
|
||||
}
|
||||
return $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all options from the component definition
|
||||
* mixes in the defaults from the defaults method and
|
||||
* then injects all additional mixins, defined in the
|
||||
* component options.
|
||||
*
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public static function setup(string $type): array
|
||||
{
|
||||
// load component definition
|
||||
$definition = static::load($type);
|
||||
/**
|
||||
* Loads all options from the component definition
|
||||
* mixes in the defaults from the defaults method and
|
||||
* then injects all additional mixins, defined in the
|
||||
* component options.
|
||||
*
|
||||
* @param string $type
|
||||
* @return array
|
||||
*/
|
||||
public static function setup(string $type): array
|
||||
{
|
||||
// load component definition
|
||||
$definition = static::load($type);
|
||||
|
||||
if (isset($definition['extends']) === true) {
|
||||
// extend other definitions
|
||||
$options = array_replace_recursive(static::defaults(), static::load($definition['extends']), $definition);
|
||||
} else {
|
||||
// inject defaults
|
||||
$options = array_replace_recursive(static::defaults(), $definition);
|
||||
}
|
||||
if (isset($definition['extends']) === true) {
|
||||
// extend other definitions
|
||||
$options = array_replace_recursive(static::defaults(), static::load($definition['extends']), $definition);
|
||||
} else {
|
||||
// inject defaults
|
||||
$options = array_replace_recursive(static::defaults(), $definition);
|
||||
}
|
||||
|
||||
// inject mixins
|
||||
if (isset($options['mixins']) === true) {
|
||||
foreach ($options['mixins'] as $mixin) {
|
||||
if (isset(static::$mixins[$mixin]) === true) {
|
||||
if (is_string(static::$mixins[$mixin]) === true) {
|
||||
// resolve a path to a mixin on demand
|
||||
static::$mixins[$mixin] = include static::$mixins[$mixin];
|
||||
}
|
||||
// inject mixins
|
||||
if (isset($options['mixins']) === true) {
|
||||
foreach ($options['mixins'] as $mixin) {
|
||||
if (isset(static::$mixins[$mixin]) === true) {
|
||||
if (is_string(static::$mixins[$mixin]) === true) {
|
||||
// resolve a path to a mixin on demand
|
||||
static::$mixins[$mixin] = include static::$mixins[$mixin];
|
||||
}
|
||||
|
||||
$options = array_replace_recursive(static::$mixins[$mixin], $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
$options = array_replace_recursive(static::$mixins[$mixin], $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all props and computed props to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
if (is_a($this->options['toArray'] ?? null, 'Closure') === true) {
|
||||
return $this->options['toArray']->call($this);
|
||||
}
|
||||
/**
|
||||
* Converts all props and computed props to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
if (is_a($this->options['toArray'] ?? null, 'Closure') === true) {
|
||||
return $this->options['toArray']->call($this);
|
||||
}
|
||||
|
||||
$array = array_merge($this->attrs, $this->props, $this->computed);
|
||||
$array = array_merge($this->attrs, $this->props, $this->computed);
|
||||
|
||||
ksort($array);
|
||||
ksort($array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
@@ -14,8 +14,8 @@ namespace Kirby\Toolkit;
|
||||
*/
|
||||
class Config extends Silo
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $data = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $data = [];
|
||||
}
|
||||
|
@@ -19,49 +19,49 @@ use ReflectionFunction;
|
||||
*/
|
||||
class Controller
|
||||
{
|
||||
protected $function;
|
||||
protected $function;
|
||||
|
||||
public function __construct(Closure $function)
|
||||
{
|
||||
$this->function = $function;
|
||||
}
|
||||
public function __construct(Closure $function)
|
||||
{
|
||||
$this->function = $function;
|
||||
}
|
||||
|
||||
public function arguments(array $data = []): array
|
||||
{
|
||||
$info = new ReflectionFunction($this->function);
|
||||
$args = [];
|
||||
public function arguments(array $data = []): array
|
||||
{
|
||||
$info = new ReflectionFunction($this->function);
|
||||
$args = [];
|
||||
|
||||
foreach ($info->getParameters() as $parameter) {
|
||||
$name = $parameter->getName();
|
||||
$args[] = $data[$name] ?? null;
|
||||
}
|
||||
foreach ($info->getParameters() as $parameter) {
|
||||
$name = $parameter->getName();
|
||||
$args[] = $data[$name] ?? null;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
|
||||
public function call($bind = null, $data = [])
|
||||
{
|
||||
$args = $this->arguments($data);
|
||||
public function call($bind = null, $data = [])
|
||||
{
|
||||
$args = $this->arguments($data);
|
||||
|
||||
if ($bind === null) {
|
||||
return call_user_func($this->function, ...$args);
|
||||
}
|
||||
if ($bind === null) {
|
||||
return call_user_func($this->function, ...$args);
|
||||
}
|
||||
|
||||
return $this->function->call($bind, ...$args);
|
||||
}
|
||||
return $this->function->call($bind, ...$args);
|
||||
}
|
||||
|
||||
public static function load(string $file)
|
||||
{
|
||||
if (is_file($file) === false) {
|
||||
return null;
|
||||
}
|
||||
public static function load(string $file)
|
||||
{
|
||||
if (is_file($file) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$function = F::load($file);
|
||||
$function = F::load($file);
|
||||
|
||||
if (is_a($function, 'Closure') === false) {
|
||||
return null;
|
||||
}
|
||||
if (is_a($function, 'Closure') === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new static($function);
|
||||
}
|
||||
return new static($function);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -23,140 +23,140 @@ use Laminas\Escaper\Escaper;
|
||||
*/
|
||||
class Escape
|
||||
{
|
||||
/**
|
||||
* The internal singleton escaper instance
|
||||
*
|
||||
* @var \Laminas\Escaper\Escaper
|
||||
*/
|
||||
protected static $escaper;
|
||||
/**
|
||||
* The internal singleton escaper instance
|
||||
*
|
||||
* @var \Laminas\Escaper\Escaper
|
||||
*/
|
||||
protected static $escaper;
|
||||
|
||||
/**
|
||||
* Escape common HTML attributes data
|
||||
*
|
||||
* This can be used to put untrusted data into typical attribute values
|
||||
* like width, name, value, etc.
|
||||
*
|
||||
* This should not be used for complex attributes like href, src, style,
|
||||
* or any of the event handlers like onmouseover.
|
||||
* Use esc($string, 'js') for event handler attributes, esc($string, 'url')
|
||||
* for src attributes and esc($string, 'css') for style attributes.
|
||||
*
|
||||
* <div attr=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...>content</div>
|
||||
* <div attr='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'>content</div>
|
||||
* <div attr="...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">content</div>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function attr($string)
|
||||
{
|
||||
return static::escaper()->escapeHtmlAttr($string);
|
||||
}
|
||||
/**
|
||||
* Escape common HTML attributes data
|
||||
*
|
||||
* This can be used to put untrusted data into typical attribute values
|
||||
* like width, name, value, etc.
|
||||
*
|
||||
* This should not be used for complex attributes like href, src, style,
|
||||
* or any of the event handlers like onmouseover.
|
||||
* Use esc($string, 'js') for event handler attributes, esc($string, 'url')
|
||||
* for src attributes and esc($string, 'css') for style attributes.
|
||||
*
|
||||
* <div attr=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...>content</div>
|
||||
* <div attr='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'>content</div>
|
||||
* <div attr="...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">content</div>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function attr($string)
|
||||
{
|
||||
return static::escaper()->escapeHtmlAttr($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape HTML style property values
|
||||
*
|
||||
* This can be used to put untrusted data into a stylesheet or a style tag.
|
||||
*
|
||||
* Stay away from putting untrusted data into complex properties like url,
|
||||
* behavior, and custom (-moz-binding). You should also not put untrusted data
|
||||
* into IE’s expression property value which allows JavaScript.
|
||||
*
|
||||
* <style>selector { property : ...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...; } </style>
|
||||
* <style>selector { property : "...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE..."; } </style>
|
||||
* <span style="property : ...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">text</span>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function css($string)
|
||||
{
|
||||
return static::escaper()->escapeCss($string);
|
||||
}
|
||||
/**
|
||||
* Escape HTML style property values
|
||||
*
|
||||
* This can be used to put untrusted data into a stylesheet or a style tag.
|
||||
*
|
||||
* Stay away from putting untrusted data into complex properties like url,
|
||||
* behavior, and custom (-moz-binding). You should also not put untrusted data
|
||||
* into IE’s expression property value which allows JavaScript.
|
||||
*
|
||||
* <style>selector { property : ...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...; } </style>
|
||||
* <style>selector { property : "...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE..."; } </style>
|
||||
* <span style="property : ...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">text</span>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function css($string)
|
||||
{
|
||||
return static::escaper()->escapeCss($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the escaper instance (and create if needed)
|
||||
*
|
||||
* @return \Laminas\Escaper\Escaper
|
||||
*/
|
||||
protected static function escaper()
|
||||
{
|
||||
return static::$escaper ??= new Escaper('utf-8');
|
||||
}
|
||||
/**
|
||||
* Get the escaper instance (and create if needed)
|
||||
*
|
||||
* @return \Laminas\Escaper\Escaper
|
||||
*/
|
||||
protected static function escaper()
|
||||
{
|
||||
return static::$escaper ??= new Escaper('utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape HTML element content
|
||||
*
|
||||
* This can be used to put untrusted data directly into the HTML body somewhere.
|
||||
* This includes inside normal tags like div, p, b, td, etc.
|
||||
*
|
||||
* Escapes &, <, >, ", and ' with HTML entity encoding to prevent switching
|
||||
* into any execution context, such as script, style, or event handlers.
|
||||
*
|
||||
* <body>...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...</body>
|
||||
* <div>...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...</div>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function html($string)
|
||||
{
|
||||
return static::escaper()->escapeHtml($string);
|
||||
}
|
||||
/**
|
||||
* Escape HTML element content
|
||||
*
|
||||
* This can be used to put untrusted data directly into the HTML body somewhere.
|
||||
* This includes inside normal tags like div, p, b, td, etc.
|
||||
*
|
||||
* Escapes &, <, >, ", and ' with HTML entity encoding to prevent switching
|
||||
* into any execution context, such as script, style, or event handlers.
|
||||
*
|
||||
* <body>...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...</body>
|
||||
* <div>...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...</div>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function html($string)
|
||||
{
|
||||
return static::escaper()->escapeHtml($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape JavaScript data values
|
||||
*
|
||||
* This can be used to put dynamically generated JavaScript code
|
||||
* into both script blocks and event-handler attributes.
|
||||
*
|
||||
* <script>alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')</script>
|
||||
* <script>x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'</script>
|
||||
* <div onmouseover="x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'"</div>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function js($string)
|
||||
{
|
||||
return static::escaper()->escapeJs($string);
|
||||
}
|
||||
/**
|
||||
* Escape JavaScript data values
|
||||
*
|
||||
* This can be used to put dynamically generated JavaScript code
|
||||
* into both script blocks and event-handler attributes.
|
||||
*
|
||||
* <script>alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')</script>
|
||||
* <script>x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'</script>
|
||||
* <div onmouseover="x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'"</div>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function js($string)
|
||||
{
|
||||
return static::escaper()->escapeJs($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape URL parameter values
|
||||
*
|
||||
* This can be used to put untrusted data into HTTP GET parameter values.
|
||||
* This should not be used to escape an entire URI.
|
||||
*
|
||||
* <a href="http://www.somesite.com?test=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">link</a>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function url($string)
|
||||
{
|
||||
return rawurlencode($string);
|
||||
}
|
||||
/**
|
||||
* Escape URL parameter values
|
||||
*
|
||||
* This can be used to put untrusted data into HTTP GET parameter values.
|
||||
* This should not be used to escape an entire URI.
|
||||
*
|
||||
* <a href="http://www.somesite.com?test=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">link</a>
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function url($string)
|
||||
{
|
||||
return rawurlencode($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape XML element content
|
||||
*
|
||||
* Removes offending characters that could be wrongfully interpreted as XML markup.
|
||||
*
|
||||
* The following characters are reserved in XML and will be replaced with their
|
||||
* corresponding XML entities:
|
||||
*
|
||||
* ' is replaced with '
|
||||
* " is replaced with "
|
||||
* & is replaced with &
|
||||
* < is replaced with <
|
||||
* > is replaced with >
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function xml($string)
|
||||
{
|
||||
return htmlspecialchars($string, ENT_QUOTES | ENT_XML1, 'UTF-8');
|
||||
}
|
||||
/**
|
||||
* Escape XML element content
|
||||
*
|
||||
* Removes offending characters that could be wrongfully interpreted as XML markup.
|
||||
*
|
||||
* The following characters are reserved in XML and will be replaced with their
|
||||
* corresponding XML entities:
|
||||
*
|
||||
* ' is replaced with '
|
||||
* " is replaced with "
|
||||
* & is replaced with &
|
||||
* < is replaced with <
|
||||
* > is replaced with >
|
||||
*
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
public static function xml($string)
|
||||
{
|
||||
return htmlspecialchars($string, ENT_QUOTES | ENT_XML1, 'UTF-8');
|
||||
}
|
||||
}
|
||||
|
@@ -14,23 +14,23 @@ namespace Kirby\Toolkit;
|
||||
*/
|
||||
abstract class Facade
|
||||
{
|
||||
/**
|
||||
* Returns the instance that should be
|
||||
* available statically
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public static function instance();
|
||||
/**
|
||||
* Returns the instance that should be
|
||||
* available statically
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public static function instance();
|
||||
|
||||
/**
|
||||
* Proxy for all public instance calls
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic(string $method, array $args = null)
|
||||
{
|
||||
return static::instance()->$method(...$args);
|
||||
}
|
||||
/**
|
||||
* Proxy for all public instance calls
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic(string $method, array $args = null)
|
||||
{
|
||||
return static::instance()->$method(...$args);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -16,280 +16,293 @@ use NumberFormatter;
|
||||
*/
|
||||
class I18n
|
||||
{
|
||||
/**
|
||||
* Custom loader function
|
||||
*
|
||||
* @var Closure
|
||||
*/
|
||||
public static $load = null;
|
||||
/**
|
||||
* Custom loader function
|
||||
*
|
||||
* @var Closure
|
||||
*/
|
||||
public static $load = null;
|
||||
|
||||
/**
|
||||
* Current locale
|
||||
*
|
||||
* @var string|\Closure
|
||||
*/
|
||||
public static $locale = 'en';
|
||||
/**
|
||||
* Current locale
|
||||
*
|
||||
* @var string|\Closure
|
||||
*/
|
||||
public static $locale = 'en';
|
||||
|
||||
/**
|
||||
* All registered translations
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $translations = [];
|
||||
/**
|
||||
* All registered translations
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $translations = [];
|
||||
|
||||
/**
|
||||
* The fallback locale or a
|
||||
* list of fallback locales
|
||||
*
|
||||
* @var string|array|\Closure
|
||||
*/
|
||||
public static $fallback = ['en'];
|
||||
/**
|
||||
* The fallback locale or a
|
||||
* list of fallback locales
|
||||
*
|
||||
* @var string|array|\Closure
|
||||
*/
|
||||
public static $fallback = ['en'];
|
||||
|
||||
/**
|
||||
* Cache of `NumberFormatter` objects by locale
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $decimalsFormatters = [];
|
||||
/**
|
||||
* Cache of `NumberFormatter` objects by locale
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $decimalsFormatters = [];
|
||||
|
||||
/**
|
||||
* Returns the list of fallback locales
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function fallbacks(): array
|
||||
{
|
||||
if (
|
||||
is_array(static::$fallback) === true ||
|
||||
is_string(static::$fallback) === true
|
||||
) {
|
||||
return A::wrap(static::$fallback);
|
||||
}
|
||||
/**
|
||||
* Returns the list of fallback locales
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function fallbacks(): array
|
||||
{
|
||||
if (
|
||||
is_array(static::$fallback) === true ||
|
||||
is_string(static::$fallback) === true
|
||||
) {
|
||||
return A::wrap(static::$fallback);
|
||||
}
|
||||
|
||||
if (is_callable(static::$fallback) === true) {
|
||||
return static::$fallback = A::wrap((static::$fallback)());
|
||||
}
|
||||
if (is_callable(static::$fallback) === true) {
|
||||
return static::$fallback = A::wrap((static::$fallback)());
|
||||
}
|
||||
|
||||
return static::$fallback = ['en'];
|
||||
}
|
||||
return static::$fallback = ['en'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns singular or plural
|
||||
* depending on the given number
|
||||
*
|
||||
* @param int $count
|
||||
* @param bool $none If true, 'none' will be returned if the count is 0
|
||||
* @return string
|
||||
*/
|
||||
public static function form(int $count, bool $none = false): string
|
||||
{
|
||||
if ($none === true && $count === 0) {
|
||||
return 'none';
|
||||
}
|
||||
/**
|
||||
* Returns singular or plural
|
||||
* depending on the given number
|
||||
*
|
||||
* @param int $count
|
||||
* @param bool $none If true, 'none' will be returned if the count is 0
|
||||
* @return string
|
||||
*/
|
||||
public static function form(int $count, bool $none = false): string
|
||||
{
|
||||
if ($none === true && $count === 0) {
|
||||
return 'none';
|
||||
}
|
||||
|
||||
return $count === 1 ? 'singular' : 'plural';
|
||||
}
|
||||
return $count === 1 ? 'singular' : 'plural';
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a number
|
||||
*
|
||||
* @param int|float $number
|
||||
* @param string $locale
|
||||
* @return string
|
||||
*/
|
||||
public static function formatNumber($number, string $locale = null): string
|
||||
{
|
||||
$locale ??= static::locale();
|
||||
/**
|
||||
* Formats a number
|
||||
*
|
||||
* @param int|float $number
|
||||
* @param string $locale
|
||||
* @return string
|
||||
*/
|
||||
public static function formatNumber($number, string $locale = null): string
|
||||
{
|
||||
$locale ??= static::locale();
|
||||
|
||||
$formatter = static::decimalNumberFormatter($locale);
|
||||
if ($formatter !== null) {
|
||||
$number = $formatter->format($number);
|
||||
}
|
||||
$formatter = static::decimalNumberFormatter($locale);
|
||||
if ($formatter !== null) {
|
||||
$number = $formatter->format($number);
|
||||
}
|
||||
|
||||
return (string)$number;
|
||||
}
|
||||
return (string)$number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locale code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function locale(): string
|
||||
{
|
||||
if (is_string(static::$locale) === true) {
|
||||
return static::$locale;
|
||||
}
|
||||
/**
|
||||
* Returns the locale code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function locale(): string
|
||||
{
|
||||
if (is_string(static::$locale) === true) {
|
||||
return static::$locale;
|
||||
}
|
||||
|
||||
if (is_callable(static::$locale) === true) {
|
||||
return static::$locale = (static::$locale)();
|
||||
}
|
||||
if (is_callable(static::$locale) === true) {
|
||||
return static::$locale = (static::$locale)();
|
||||
}
|
||||
|
||||
return static::$locale = 'en';
|
||||
}
|
||||
return static::$locale = 'en';
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a given message
|
||||
* according to the currently set locale
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param string|array|null $fallback
|
||||
* @param string|null $locale
|
||||
* @return string|array|null
|
||||
*/
|
||||
public static function translate($key, $fallback = null, string $locale = null)
|
||||
{
|
||||
$locale ??= static::locale();
|
||||
/**
|
||||
* Translates a given message
|
||||
* according to the currently set locale
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param string|array|null $fallback
|
||||
* @param string|null $locale
|
||||
* @return string|array|null
|
||||
*/
|
||||
public static function translate($key, $fallback = null, string $locale = null)
|
||||
{
|
||||
$locale ??= static::locale();
|
||||
|
||||
if (is_array($key) === true) {
|
||||
if (isset($key[$locale])) {
|
||||
return $key[$locale];
|
||||
}
|
||||
if (is_array($fallback)) {
|
||||
return $fallback[$locale] ?? $fallback['en'] ?? reset($fallback);
|
||||
}
|
||||
return $fallback;
|
||||
}
|
||||
if (is_array($key) === true) {
|
||||
// try to use actual locale
|
||||
if (isset($key[$locale])) {
|
||||
return $key[$locale];
|
||||
}
|
||||
// try to use language code, e.g. `es` when locale is `es_ES`
|
||||
$lang = Str::before($locale, '_');
|
||||
if (isset($key[$lang])) {
|
||||
return $key[$lang];
|
||||
}
|
||||
// use fallback
|
||||
if (is_array($fallback)) {
|
||||
return $fallback[$locale] ?? $fallback['en'] ?? reset($fallback);
|
||||
}
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
if ($translation = static::translation($locale)[$key] ?? null) {
|
||||
return $translation;
|
||||
}
|
||||
if ($translation = static::translation($locale)[$key] ?? null) {
|
||||
return $translation;
|
||||
}
|
||||
|
||||
if ($fallback !== null) {
|
||||
return $fallback;
|
||||
}
|
||||
if ($fallback !== null) {
|
||||
return $fallback;
|
||||
}
|
||||
|
||||
foreach (static::fallbacks() as $fallback) {
|
||||
// skip locales we have already tried
|
||||
if ($locale === $fallback) {
|
||||
continue;
|
||||
}
|
||||
foreach (static::fallbacks() as $fallback) {
|
||||
// skip locales we have already tried
|
||||
if ($locale === $fallback) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($translation = static::translation($fallback)[$key] ?? null) {
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
if ($translation = static::translation($fallback)[$key] ?? null) {
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate by key and then replace
|
||||
* placeholders in the text
|
||||
*
|
||||
* @param string $key
|
||||
* @param string|array|null $fallback
|
||||
* @param array|null $replace
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
public static function template(string $key, $fallback = null, ?array $replace = null, ?string $locale = null): string
|
||||
{
|
||||
if (is_array($fallback) === true) {
|
||||
$replace = $fallback;
|
||||
$fallback = null;
|
||||
$locale = null;
|
||||
}
|
||||
/**
|
||||
* Translate by key and then replace
|
||||
* placeholders in the text
|
||||
*
|
||||
* @param string $key
|
||||
* @param string|array|null $fallback
|
||||
* @param array|null $replace
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
public static function template(string $key, $fallback = null, ?array $replace = null, ?string $locale = null): string
|
||||
{
|
||||
if (is_array($fallback) === true) {
|
||||
$replace = $fallback;
|
||||
$fallback = null;
|
||||
$locale = null;
|
||||
}
|
||||
|
||||
$template = static::translate($key, $fallback, $locale);
|
||||
return Str::template($template, $replace, [
|
||||
'fallback' => '-',
|
||||
'start' => '{',
|
||||
'end' => '}'
|
||||
]);
|
||||
}
|
||||
$template = static::translate($key, $fallback, $locale);
|
||||
return Str::template($template, $replace, [
|
||||
'fallback' => '-',
|
||||
'start' => '{',
|
||||
'end' => '}'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
$locale ??= static::locale();
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
$locale ??= static::locale();
|
||||
|
||||
if (isset(static::$translations[$locale]) === true) {
|
||||
return static::$translations[$locale];
|
||||
}
|
||||
if (isset(static::$translations[$locale]) === true) {
|
||||
return static::$translations[$locale];
|
||||
}
|
||||
|
||||
if (is_a(static::$load, 'Closure') === true) {
|
||||
return static::$translations[$locale] = (static::$load)($locale);
|
||||
}
|
||||
if (is_a(static::$load, 'Closure') === true) {
|
||||
return static::$translations[$locale] = (static::$load)($locale);
|
||||
}
|
||||
|
||||
return static::$translations[$locale] = [];
|
||||
}
|
||||
// try to use language code, e.g. `es` when locale is `es_ES`
|
||||
$lang = Str::before($locale, '_');
|
||||
if (isset(static::$translations[$lang]) === true) {
|
||||
return static::$translations[$lang];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all loaded or defined translations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function translations(): array
|
||||
{
|
||||
return static::$translations;
|
||||
}
|
||||
return static::$translations[$locale] = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns (and creates) a decimal number formatter for a given locale
|
||||
*
|
||||
* @return \NumberFormatter|null
|
||||
*/
|
||||
protected static function decimalNumberFormatter(string $locale): ?NumberFormatter
|
||||
{
|
||||
if (isset(static::$decimalsFormatters[$locale])) {
|
||||
return static::$decimalsFormatters[$locale];
|
||||
}
|
||||
/**
|
||||
* Returns all loaded or defined translations
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function translations(): array
|
||||
{
|
||||
return static::$translations;
|
||||
}
|
||||
|
||||
if (extension_loaded('intl') !== true || class_exists('NumberFormatter') !== true) {
|
||||
return null; // @codeCoverageIgnore
|
||||
}
|
||||
/**
|
||||
* Returns (and creates) a decimal number formatter for a given locale
|
||||
*
|
||||
* @return \NumberFormatter|null
|
||||
*/
|
||||
protected static function decimalNumberFormatter(string $locale): ?NumberFormatter
|
||||
{
|
||||
if (isset(static::$decimalsFormatters[$locale])) {
|
||||
return static::$decimalsFormatters[$locale];
|
||||
}
|
||||
|
||||
return static::$decimalsFormatters[$locale] = new NumberFormatter($locale, NumberFormatter::DECIMAL);
|
||||
}
|
||||
if (extension_loaded('intl') !== true || class_exists('NumberFormatter') !== true) {
|
||||
return null; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates amounts
|
||||
*
|
||||
* Translation definition options:
|
||||
* - Translation is a simple string: `{{ count }}` gets replaced in the template
|
||||
* - Translation is an array with a value for each count: Chooses the correct template and
|
||||
* replaces `{{ count }}` in the template; if no specific template for the input count is
|
||||
* defined, the template that is defined last in the translation array is used
|
||||
* - Translation is a callback with a `$count` argument: Returns the callback return value
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $count
|
||||
* @param string|null $locale
|
||||
* @param bool $formatNumber If set to `false`, the count is not formatted
|
||||
* @return mixed
|
||||
*/
|
||||
public static function translateCount(string $key, int $count, string $locale = null, bool $formatNumber = true)
|
||||
{
|
||||
$locale ??= static::locale();
|
||||
$translation = static::translate($key, null, $locale);
|
||||
return static::$decimalsFormatters[$locale] = new NumberFormatter($locale, NumberFormatter::DECIMAL);
|
||||
}
|
||||
|
||||
if ($translation === null) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Translates amounts
|
||||
*
|
||||
* Translation definition options:
|
||||
* - Translation is a simple string: `{{ count }}` gets replaced in the template
|
||||
* - Translation is an array with a value for each count: Chooses the correct template and
|
||||
* replaces `{{ count }}` in the template; if no specific template for the input count is
|
||||
* defined, the template that is defined last in the translation array is used
|
||||
* - Translation is a callback with a `$count` argument: Returns the callback return value
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $count
|
||||
* @param string|null $locale
|
||||
* @param bool $formatNumber If set to `false`, the count is not formatted
|
||||
* @return mixed
|
||||
*/
|
||||
public static function translateCount(string $key, int $count, string $locale = null, bool $formatNumber = true)
|
||||
{
|
||||
$locale ??= static::locale();
|
||||
$translation = static::translate($key, null, $locale);
|
||||
|
||||
if (is_a($translation, 'Closure') === true) {
|
||||
return $translation($count);
|
||||
}
|
||||
if ($translation === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_string($translation) === true) {
|
||||
$message = $translation;
|
||||
} elseif (isset($translation[$count]) === true) {
|
||||
$message = $translation[$count];
|
||||
} else {
|
||||
$message = end($translation);
|
||||
}
|
||||
if (is_a($translation, 'Closure') === true) {
|
||||
return $translation($count);
|
||||
}
|
||||
|
||||
if ($formatNumber === true) {
|
||||
$count = static::formatNumber($count, $locale);
|
||||
}
|
||||
if (is_string($translation) === true) {
|
||||
$message = $translation;
|
||||
} elseif (isset($translation[$count]) === true) {
|
||||
$message = $translation[$count];
|
||||
} else {
|
||||
$message = end($translation);
|
||||
}
|
||||
|
||||
return str_replace('{{ count }}', $count, $message);
|
||||
}
|
||||
if ($formatNumber === true) {
|
||||
$count = static::formatNumber($count, $locale);
|
||||
}
|
||||
|
||||
return str_replace('{{ count }}', $count, $message);
|
||||
}
|
||||
}
|
||||
|
@@ -18,164 +18,164 @@ use IteratorAggregate;
|
||||
*/
|
||||
class Iterator implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* The data array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $data = [];
|
||||
/**
|
||||
* The data array
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $data = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an iterator for the items.
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->data);
|
||||
}
|
||||
/**
|
||||
* Get an iterator for the items.
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
public function getIterator(): ArrayIterator
|
||||
{
|
||||
return new ArrayIterator($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->data);
|
||||
}
|
||||
/**
|
||||
* Returns the current key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return key($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of all keys
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function keys(): array
|
||||
{
|
||||
return array_keys($this->data);
|
||||
}
|
||||
/**
|
||||
* Returns an array of all keys
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function keys(): array
|
||||
{
|
||||
return array_keys($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current element
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->data);
|
||||
}
|
||||
/**
|
||||
* Returns the current element
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return current($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor to the previous element
|
||||
* and returns it
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function prev()
|
||||
{
|
||||
return prev($this->data);
|
||||
}
|
||||
/**
|
||||
* Moves the cursor to the previous element
|
||||
* and returns it
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function prev()
|
||||
{
|
||||
return prev($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor to the next element
|
||||
* and returns it
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->data);
|
||||
}
|
||||
/**
|
||||
* Moves the cursor to the next element
|
||||
* and returns it
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor to the first element
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->data);
|
||||
}
|
||||
/**
|
||||
* Moves the cursor to the first element
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
reset($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current element is valid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
return $this->current() !== false;
|
||||
}
|
||||
/**
|
||||
* Checks if the current element is valid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
return $this->current() !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts all elements
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
/**
|
||||
* Counts all elements
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find the index number for the given element
|
||||
*
|
||||
* @param mixed $needle the element to search for
|
||||
* @return int|false the index (int) of the element or false
|
||||
*/
|
||||
public function indexOf($needle)
|
||||
{
|
||||
return array_search($needle, array_values($this->data));
|
||||
}
|
||||
/**
|
||||
* Tries to find the index number for the given element
|
||||
*
|
||||
* @param mixed $needle the element to search for
|
||||
* @return int|false the index (int) of the element or false
|
||||
*/
|
||||
public function indexOf($needle)
|
||||
{
|
||||
return array_search($needle, array_values($this->data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find the key for the given element
|
||||
*
|
||||
* @param mixed $needle the element to search for
|
||||
* @return string|false the name of the key or false
|
||||
*/
|
||||
public function keyOf($needle)
|
||||
{
|
||||
return array_search($needle, $this->data);
|
||||
}
|
||||
/**
|
||||
* Tries to find the key for the given element
|
||||
*
|
||||
* @param mixed $needle the element to search for
|
||||
* @return string|false the name of the key or false
|
||||
*/
|
||||
public function keyOf($needle)
|
||||
{
|
||||
return array_search($needle, $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks by key if an element is included
|
||||
*
|
||||
* @param mixed $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key): bool
|
||||
{
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
/**
|
||||
* Checks by key if an element is included
|
||||
*
|
||||
* @param mixed $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key): bool
|
||||
{
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current key is set
|
||||
*
|
||||
* @param mixed $key the key to check
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($key): bool
|
||||
{
|
||||
return $this->has($key);
|
||||
}
|
||||
/**
|
||||
* Checks if the current key is set
|
||||
*
|
||||
* @param mixed $key the key to check
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($key): bool
|
||||
{
|
||||
return $this->has($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified var_dump output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
/**
|
||||
* Simplified var_dump output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
|
@@ -17,167 +17,167 @@ use Kirby\Exception\InvalidArgumentException;
|
||||
*/
|
||||
class Locale
|
||||
{
|
||||
/**
|
||||
* List of all locale constants supported by PHP
|
||||
*/
|
||||
public const LOCALE_CONSTANTS = [
|
||||
'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY',
|
||||
'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'
|
||||
];
|
||||
/**
|
||||
* List of all locale constants supported by PHP
|
||||
*/
|
||||
public const LOCALE_CONSTANTS = [
|
||||
'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY',
|
||||
'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'
|
||||
];
|
||||
|
||||
/**
|
||||
* Converts a normalized locale array to an array with the
|
||||
* locale constants replaced with their string representations
|
||||
*
|
||||
* @param array $locale
|
||||
* @return array
|
||||
*/
|
||||
public static function export(array $locale): array
|
||||
{
|
||||
$constants = static::supportedConstants(true);
|
||||
/**
|
||||
* Converts a normalized locale array to an array with the
|
||||
* locale constants replaced with their string representations
|
||||
*
|
||||
* @param array $locale
|
||||
* @return array
|
||||
*/
|
||||
public static function export(array $locale): array
|
||||
{
|
||||
$constants = static::supportedConstants(true);
|
||||
|
||||
// replace the keys in the locale data array with the locale names
|
||||
$return = [];
|
||||
foreach ($locale as $key => $value) {
|
||||
if (isset($constants[$key]) === true) {
|
||||
// the key is a valid constant,
|
||||
// replace it with its string representation
|
||||
$return[$constants[$key]] = $value;
|
||||
} else {
|
||||
// not found, keep it as-is
|
||||
$return[$key] = $value;
|
||||
}
|
||||
}
|
||||
// replace the keys in the locale data array with the locale names
|
||||
$return = [];
|
||||
foreach ($locale as $key => $value) {
|
||||
if (isset($constants[$key]) === true) {
|
||||
// the key is a valid constant,
|
||||
// replace it with its string representation
|
||||
$return[$constants[$key]] = $value;
|
||||
} else {
|
||||
// not found, keep it as-is
|
||||
$return[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current locale value for
|
||||
* a specified or for all locale categories
|
||||
* @since 3.5.6
|
||||
*
|
||||
* @param int|string $category Locale category constant or constant name
|
||||
* @return array|string Associative array if `LC_ALL` was passed (default), otherwise string
|
||||
*
|
||||
* @throws \Kirby\Exception\Exception If the locale cannot be determined
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the provided locale category is invalid
|
||||
*/
|
||||
public static function get($category = LC_ALL)
|
||||
{
|
||||
$normalizedCategory = static::normalizeConstant($category);
|
||||
/**
|
||||
* Returns the current locale value for
|
||||
* a specified or for all locale categories
|
||||
* @since 3.5.6
|
||||
*
|
||||
* @param int|string $category Locale category constant or constant name
|
||||
* @return array|string Associative array if `LC_ALL` was passed (default), otherwise string
|
||||
*
|
||||
* @throws \Kirby\Exception\Exception If the locale cannot be determined
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the provided locale category is invalid
|
||||
*/
|
||||
public static function get($category = LC_ALL)
|
||||
{
|
||||
$normalizedCategory = static::normalizeConstant($category);
|
||||
|
||||
if (is_int($normalizedCategory) !== true) {
|
||||
throw new InvalidArgumentException('Invalid locale category "' . $category . '"');
|
||||
}
|
||||
if (is_int($normalizedCategory) !== true) {
|
||||
throw new InvalidArgumentException('Invalid locale category "' . $category . '"');
|
||||
}
|
||||
|
||||
if ($normalizedCategory !== LC_ALL) {
|
||||
// `setlocale(..., 0)` actually *gets* the locale
|
||||
$locale = setlocale($normalizedCategory, 0);
|
||||
if ($normalizedCategory !== LC_ALL) {
|
||||
// `setlocale(..., 0)` actually *gets* the locale
|
||||
$locale = setlocale($normalizedCategory, 0);
|
||||
|
||||
if (is_string($locale) !== true) {
|
||||
throw new Exception('Could not determine locale for category "' . $category . '"');
|
||||
}
|
||||
if (is_string($locale) !== true) {
|
||||
throw new Exception('Could not determine locale for category "' . $category . '"');
|
||||
}
|
||||
|
||||
return $locale;
|
||||
}
|
||||
return $locale;
|
||||
}
|
||||
|
||||
// no specific `$category` was passed, make a list of all locales
|
||||
$array = [];
|
||||
foreach (static::supportedConstants() as $constant => $name) {
|
||||
// `setlocale(..., 0)` actually *gets* the locale
|
||||
$array[$constant] = setlocale($constant, '0');
|
||||
}
|
||||
// no specific `$category` was passed, make a list of all locales
|
||||
$array = [];
|
||||
foreach (static::supportedConstants() as $constant => $name) {
|
||||
// `setlocale(..., 0)` actually *gets* the locale
|
||||
$array[$constant] = setlocale($constant, '0');
|
||||
}
|
||||
|
||||
// if all values are the same, we can use `LC_ALL`
|
||||
// instead of a long array with all constants
|
||||
if (count(array_unique($array)) === 1) {
|
||||
return [
|
||||
LC_ALL => array_shift($array)
|
||||
];
|
||||
}
|
||||
// if all values are the same, we can use `LC_ALL`
|
||||
// instead of a long array with all constants
|
||||
if (count(array_unique($array)) === 1) {
|
||||
return [
|
||||
LC_ALL => array_shift($array)
|
||||
];
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a locale string or an array with constant or
|
||||
* string keys to a normalized constant => value array
|
||||
*
|
||||
* @param array|string $locale
|
||||
* @return array
|
||||
*/
|
||||
public static function normalize($locale): array
|
||||
{
|
||||
if (is_array($locale)) {
|
||||
// replace string constant keys with the constant values
|
||||
$convertedLocale = [];
|
||||
foreach ($locale as $key => $value) {
|
||||
$convertedLocale[static::normalizeConstant($key)] = $value;
|
||||
}
|
||||
/**
|
||||
* Converts a locale string or an array with constant or
|
||||
* string keys to a normalized constant => value array
|
||||
*
|
||||
* @param array|string $locale
|
||||
* @return array
|
||||
*/
|
||||
public static function normalize($locale): array
|
||||
{
|
||||
if (is_array($locale)) {
|
||||
// replace string constant keys with the constant values
|
||||
$convertedLocale = [];
|
||||
foreach ($locale as $key => $value) {
|
||||
$convertedLocale[static::normalizeConstant($key)] = $value;
|
||||
}
|
||||
|
||||
return $convertedLocale;
|
||||
} elseif (is_string($locale)) {
|
||||
return [LC_ALL => $locale];
|
||||
} else {
|
||||
throw new InvalidArgumentException('Locale must be string or array');
|
||||
}
|
||||
}
|
||||
return $convertedLocale;
|
||||
} elseif (is_string($locale)) {
|
||||
return [LC_ALL => $locale];
|
||||
} else {
|
||||
throw new InvalidArgumentException('Locale must be string or array');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the PHP locale with a locale string or
|
||||
* an array with constant or string keys
|
||||
*
|
||||
* @param array|string $locale
|
||||
* @return void
|
||||
*/
|
||||
public static function set($locale): void
|
||||
{
|
||||
$locale = static::normalize($locale);
|
||||
/**
|
||||
* Sets the PHP locale with a locale string or
|
||||
* an array with constant or string keys
|
||||
*
|
||||
* @param array|string $locale
|
||||
* @return void
|
||||
*/
|
||||
public static function set($locale): void
|
||||
{
|
||||
$locale = static::normalize($locale);
|
||||
|
||||
foreach ($locale as $key => $value) {
|
||||
setlocale($key, $value);
|
||||
}
|
||||
}
|
||||
foreach ($locale as $key => $value) {
|
||||
setlocale($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to convert an `LC_*` constant name
|
||||
* to its constant value
|
||||
*
|
||||
* @param int|string $constant
|
||||
* @return int|string
|
||||
*/
|
||||
protected static function normalizeConstant($constant)
|
||||
{
|
||||
if (is_string($constant) === true && Str::startsWith($constant, 'LC_') === true) {
|
||||
return constant($constant);
|
||||
}
|
||||
/**
|
||||
* Tries to convert an `LC_*` constant name
|
||||
* to its constant value
|
||||
*
|
||||
* @param int|string $constant
|
||||
* @return int|string
|
||||
*/
|
||||
protected static function normalizeConstant($constant)
|
||||
{
|
||||
if (is_string($constant) === true && Str::startsWith($constant, 'LC_') === true) {
|
||||
return constant($constant);
|
||||
}
|
||||
|
||||
// already an int or we cannot convert it safely
|
||||
return $constant;
|
||||
}
|
||||
// already an int or we cannot convert it safely
|
||||
return $constant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an associative array with the locales
|
||||
* that are actually supported on this system
|
||||
*
|
||||
* @param bool $withAll If set to `true`, `LC_ALL` is returned as well
|
||||
* @return array
|
||||
*/
|
||||
protected static function supportedConstants(bool $withAll = false): array
|
||||
{
|
||||
$names = static::LOCALE_CONSTANTS;
|
||||
if ($withAll === true) {
|
||||
array_unshift($names, 'LC_ALL');
|
||||
}
|
||||
/**
|
||||
* Builds an associative array with the locales
|
||||
* that are actually supported on this system
|
||||
*
|
||||
* @param bool $withAll If set to `true`, `LC_ALL` is returned as well
|
||||
* @return array
|
||||
*/
|
||||
protected static function supportedConstants(bool $withAll = false): array
|
||||
{
|
||||
$names = static::LOCALE_CONSTANTS;
|
||||
if ($withAll === true) {
|
||||
array_unshift($names, 'LC_ALL');
|
||||
}
|
||||
|
||||
$constants = [];
|
||||
foreach ($names as $name) {
|
||||
if (defined($name) === true) {
|
||||
$constants[constant($name)] = $name;
|
||||
}
|
||||
}
|
||||
$constants = [];
|
||||
foreach ($names as $name) {
|
||||
if (defined($name) === true) {
|
||||
$constants[constant($name)] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $constants;
|
||||
}
|
||||
return $constants;
|
||||
}
|
||||
}
|
||||
|
@@ -17,108 +17,108 @@ use stdClass;
|
||||
*/
|
||||
class Obj extends stdClass
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
foreach ($data as $key => $val) {
|
||||
$this->$key = $val;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
foreach ($data as $key => $val) {
|
||||
$this->$key = $val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic getter
|
||||
*
|
||||
* @param string $property
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $property, array $arguments)
|
||||
{
|
||||
return $this->$property ?? null;
|
||||
}
|
||||
/**
|
||||
* Magic getter
|
||||
*
|
||||
* @param string $property
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $property, array $arguments)
|
||||
{
|
||||
return $this->$property ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
/**
|
||||
* Improved `var_dump` output
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic property getter
|
||||
*
|
||||
* @param string $property
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $property)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Magic property getter
|
||||
*
|
||||
* @param string $property
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $property)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one or multiple properties of the object
|
||||
*
|
||||
* @param string|array $property
|
||||
* @param mixed $fallback If multiple properties are requested:
|
||||
* Associative array of fallback values per key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($property, $fallback = null)
|
||||
{
|
||||
if (is_array($property)) {
|
||||
if ($fallback === null) {
|
||||
$fallback = [];
|
||||
}
|
||||
/**
|
||||
* Gets one or multiple properties of the object
|
||||
*
|
||||
* @param string|array $property
|
||||
* @param mixed $fallback If multiple properties are requested:
|
||||
* Associative array of fallback values per key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($property, $fallback = null)
|
||||
{
|
||||
if (is_array($property)) {
|
||||
if ($fallback === null) {
|
||||
$fallback = [];
|
||||
}
|
||||
|
||||
if (!is_array($fallback)) {
|
||||
throw new InvalidArgumentException('The fallback value must be an array when getting multiple properties');
|
||||
}
|
||||
if (!is_array($fallback)) {
|
||||
throw new InvalidArgumentException('The fallback value must be an array when getting multiple properties');
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($property as $key) {
|
||||
$result[$key] = $this->$key ?? $fallback[$key] ?? null;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
$result = [];
|
||||
foreach ($property as $key) {
|
||||
$result[$key] = $this->$key ?? $fallback[$key] ?? null;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $this->$property ?? $fallback;
|
||||
}
|
||||
return $this->$property ?? $fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$result = [];
|
||||
/**
|
||||
* Converts the object to an array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ((array)$this as $key => $value) {
|
||||
if (is_object($value) === true && method_exists($value, 'toArray')) {
|
||||
$result[$key] = $value->toArray();
|
||||
} else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
foreach ((array)$this as $key => $value) {
|
||||
if (is_object($value) === true && method_exists($value, 'toArray')) {
|
||||
$result[$key] = $value->toArray();
|
||||
} else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the object to a json string
|
||||
*
|
||||
* @param mixed ...$arguments
|
||||
* @return string
|
||||
*/
|
||||
public function toJson(...$arguments): string
|
||||
{
|
||||
return json_encode($this->toArray(), ...$arguments);
|
||||
}
|
||||
/**
|
||||
* Converts the object to a json string
|
||||
*
|
||||
* @param mixed ...$arguments
|
||||
* @return string
|
||||
*/
|
||||
public function toJson(...$arguments): string
|
||||
{
|
||||
return json_encode($this->toArray(), ...$arguments);
|
||||
}
|
||||
}
|
||||
|
@@ -16,478 +16,478 @@ use Kirby\Exception\Exception;
|
||||
*/
|
||||
class Pagination
|
||||
{
|
||||
use Properties {
|
||||
setProperties as protected baseSetProperties;
|
||||
}
|
||||
use Properties {
|
||||
setProperties as protected baseSetProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current page
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $page;
|
||||
/**
|
||||
* The current page
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
* Total number of items
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $total = 0;
|
||||
/**
|
||||
* Total number of items
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $total = 0;
|
||||
|
||||
/**
|
||||
* The number of items per page
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $limit = 20;
|
||||
/**
|
||||
* The number of items per page
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $limit = 20;
|
||||
|
||||
/**
|
||||
* Whether validation of the pagination page
|
||||
* is enabled; will throw Exceptions if true
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $validate = true;
|
||||
/**
|
||||
* Whether validation of the pagination page
|
||||
* is enabled; will throw Exceptions if true
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public static $validate = true;
|
||||
|
||||
/**
|
||||
* Creates a new pagination object
|
||||
* with the given parameters
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props = [])
|
||||
{
|
||||
$this->setProperties($props);
|
||||
}
|
||||
/**
|
||||
* Creates a new pagination object
|
||||
* with the given parameters
|
||||
*
|
||||
* @param array $props
|
||||
*/
|
||||
public function __construct(array $props = [])
|
||||
{
|
||||
$this->setProperties($props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a pagination instance for the given
|
||||
* collection with a flexible argument api
|
||||
*
|
||||
* @param \Kirby\Toolkit\Collection $collection
|
||||
* @param mixed ...$arguments
|
||||
* @return static
|
||||
*/
|
||||
public static function for(Collection $collection, ...$arguments)
|
||||
{
|
||||
$a = $arguments[0] ?? null;
|
||||
$b = $arguments[1] ?? null;
|
||||
/**
|
||||
* Creates a pagination instance for the given
|
||||
* collection with a flexible argument api
|
||||
*
|
||||
* @param \Kirby\Toolkit\Collection $collection
|
||||
* @param mixed ...$arguments
|
||||
* @return static
|
||||
*/
|
||||
public static function for(Collection $collection, ...$arguments)
|
||||
{
|
||||
$a = $arguments[0] ?? null;
|
||||
$b = $arguments[1] ?? null;
|
||||
|
||||
$params = [];
|
||||
$params = [];
|
||||
|
||||
if (is_a($a, static::class) === true) {
|
||||
/**
|
||||
* First argument is a pagination/self object
|
||||
*/
|
||||
return $a;
|
||||
} elseif (is_array($a) === true) {
|
||||
if (is_a($a, static::class) === true) {
|
||||
/**
|
||||
* First argument is a pagination/self object
|
||||
*/
|
||||
return $a;
|
||||
} elseif (is_array($a) === true) {
|
||||
|
||||
/**
|
||||
* First argument is an option array
|
||||
*
|
||||
* $collection->paginate([...])
|
||||
*/
|
||||
$params = $a;
|
||||
} elseif (is_int($a) === true && $b === null) {
|
||||
/**
|
||||
* First argument is an option array
|
||||
*
|
||||
* $collection->paginate([...])
|
||||
*/
|
||||
$params = $a;
|
||||
} elseif (is_int($a) === true && $b === null) {
|
||||
|
||||
/**
|
||||
* First argument is the limit
|
||||
*
|
||||
* $collection->paginate(10)
|
||||
*/
|
||||
$params['limit'] = $a;
|
||||
} elseif (is_int($a) === true && is_int($b) === true) {
|
||||
/**
|
||||
* First argument is the limit
|
||||
*
|
||||
* $collection->paginate(10)
|
||||
*/
|
||||
$params['limit'] = $a;
|
||||
} elseif (is_int($a) === true && is_int($b) === true) {
|
||||
|
||||
/**
|
||||
* First argument is the limit,
|
||||
* second argument is the page
|
||||
*
|
||||
* $collection->paginate(10, 2)
|
||||
*/
|
||||
$params['limit'] = $a;
|
||||
$params['page'] = $b;
|
||||
} elseif (is_int($a) === true && is_array($b) === true) {
|
||||
/**
|
||||
* First argument is the limit,
|
||||
* second argument is the page
|
||||
*
|
||||
* $collection->paginate(10, 2)
|
||||
*/
|
||||
$params['limit'] = $a;
|
||||
$params['page'] = $b;
|
||||
} elseif (is_int($a) === true && is_array($b) === true) {
|
||||
|
||||
/**
|
||||
* First argument is the limit,
|
||||
* second argument are options
|
||||
*
|
||||
* $collection->paginate(10, [...])
|
||||
*/
|
||||
$params = $b;
|
||||
$params['limit'] = $a;
|
||||
}
|
||||
/**
|
||||
* First argument is the limit,
|
||||
* second argument are options
|
||||
*
|
||||
* $collection->paginate(10, [...])
|
||||
*/
|
||||
$params = $b;
|
||||
$params['limit'] = $a;
|
||||
}
|
||||
|
||||
// add the total count from the collection
|
||||
$params['total'] = $collection->count();
|
||||
// add the total count from the collection
|
||||
$params['total'] = $collection->count();
|
||||
|
||||
// remove null values to make later merges work properly
|
||||
$params = array_filter($params);
|
||||
// remove null values to make later merges work properly
|
||||
$params = array_filter($params);
|
||||
|
||||
// create the pagination instance
|
||||
return new static($params);
|
||||
}
|
||||
// create the pagination instance
|
||||
return new static($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the current page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function page(): int
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
/**
|
||||
* Getter for the current page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function page(): int
|
||||
{
|
||||
return $this->page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the total number of items
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function total(): int
|
||||
{
|
||||
return $this->total;
|
||||
}
|
||||
/**
|
||||
* Getter for the total number of items
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function total(): int
|
||||
{
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the number of items per page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function limit(): int
|
||||
{
|
||||
return $this->limit;
|
||||
}
|
||||
/**
|
||||
* Getter for the number of items per page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function limit(): int
|
||||
{
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the first item on the page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function start(): int
|
||||
{
|
||||
$index = $this->page() - 1;
|
||||
/**
|
||||
* Returns the index of the first item on the page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function start(): int
|
||||
{
|
||||
$index = $this->page() - 1;
|
||||
|
||||
if ($index < 0) {
|
||||
$index = 0;
|
||||
}
|
||||
if ($index < 0) {
|
||||
$index = 0;
|
||||
}
|
||||
|
||||
return $index * $this->limit() + 1;
|
||||
}
|
||||
return $index * $this->limit() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the last item on the page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function end(): int
|
||||
{
|
||||
$value = ($this->start() - 1) + $this->limit();
|
||||
/**
|
||||
* Returns the index of the last item on the page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function end(): int
|
||||
{
|
||||
$value = ($this->start() - 1) + $this->limit();
|
||||
|
||||
if ($value <= $this->total()) {
|
||||
return $value;
|
||||
}
|
||||
if ($value <= $this->total()) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $this->total();
|
||||
}
|
||||
return $this->total();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of pages
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function pages(): int
|
||||
{
|
||||
if ($this->total() === 0) {
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Returns the total number of pages
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function pages(): int
|
||||
{
|
||||
if ($this->total() === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)ceil($this->total() / $this->limit());
|
||||
}
|
||||
return (int)ceil($this->total() / $this->limit());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function firstPage(): int
|
||||
{
|
||||
return $this->total() === 0 ? 0 : 1;
|
||||
}
|
||||
/**
|
||||
* Returns the first page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function firstPage(): int
|
||||
{
|
||||
return $this->total() === 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function lastPage(): int
|
||||
{
|
||||
return $this->pages();
|
||||
}
|
||||
/**
|
||||
* Returns the last page
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function lastPage(): int
|
||||
{
|
||||
return $this->pages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the offset (i.e. for db queries)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function offset(): int
|
||||
{
|
||||
return $this->start() - 1;
|
||||
}
|
||||
/**
|
||||
* Returns the offset (i.e. for db queries)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function offset(): int
|
||||
{
|
||||
return $this->start() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given page exists
|
||||
*
|
||||
* @param int $page
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPage(int $page): bool
|
||||
{
|
||||
if ($page <= 0) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Checks if the given page exists
|
||||
*
|
||||
* @param int $page
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPage(int $page): bool
|
||||
{
|
||||
if ($page <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($page > $this->pages()) {
|
||||
return false;
|
||||
}
|
||||
if ($page > $this->pages()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are any pages at all
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPages(): bool
|
||||
{
|
||||
return $this->total() > $this->limit();
|
||||
}
|
||||
/**
|
||||
* Checks if there are any pages at all
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPages(): bool
|
||||
{
|
||||
return $this->total() > $this->limit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's a previous page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrevPage(): bool
|
||||
{
|
||||
return $this->page() > 1;
|
||||
}
|
||||
/**
|
||||
* Checks if there's a previous page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPrevPage(): bool
|
||||
{
|
||||
return $this->page() > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous page
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function prevPage()
|
||||
{
|
||||
return $this->hasPrevPage() ? $this->page() - 1 : null;
|
||||
}
|
||||
/**
|
||||
* Returns the previous page
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function prevPage()
|
||||
{
|
||||
return $this->hasPrevPage() ? $this->page() - 1 : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there's a next page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNextPage(): bool
|
||||
{
|
||||
return $this->end() < $this->total();
|
||||
}
|
||||
/**
|
||||
* Checks if there's a next page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasNextPage(): bool
|
||||
{
|
||||
return $this->end() < $this->total();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next page
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function nextPage()
|
||||
{
|
||||
return $this->hasNextPage() ? $this->page() + 1 : null;
|
||||
}
|
||||
/**
|
||||
* Returns the next page
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function nextPage()
|
||||
{
|
||||
return $this->hasNextPage() ? $this->page() + 1 : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current page is the first page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFirstPage(): bool
|
||||
{
|
||||
return $this->page() === $this->firstPage();
|
||||
}
|
||||
/**
|
||||
* Checks if the current page is the first page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFirstPage(): bool
|
||||
{
|
||||
return $this->page() === $this->firstPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current page is the last page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLastPage(): bool
|
||||
{
|
||||
return $this->page() === $this->lastPage();
|
||||
}
|
||||
/**
|
||||
* Checks if the current page is the last page
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLastPage(): bool
|
||||
{
|
||||
return $this->page() === $this->lastPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a range of page numbers for Google-like pagination
|
||||
*
|
||||
* @param int $range
|
||||
* @return array
|
||||
*/
|
||||
public function range(int $range = 5): array
|
||||
{
|
||||
$page = $this->page();
|
||||
$pages = $this->pages();
|
||||
$start = 1;
|
||||
$end = $pages;
|
||||
/**
|
||||
* Creates a range of page numbers for Google-like pagination
|
||||
*
|
||||
* @param int $range
|
||||
* @return array
|
||||
*/
|
||||
public function range(int $range = 5): array
|
||||
{
|
||||
$page = $this->page();
|
||||
$pages = $this->pages();
|
||||
$start = 1;
|
||||
$end = $pages;
|
||||
|
||||
if ($pages <= $range) {
|
||||
return range($start, $end);
|
||||
}
|
||||
if ($pages <= $range) {
|
||||
return range($start, $end);
|
||||
}
|
||||
|
||||
$middle = (int)floor($range/2);
|
||||
$start = $page - $middle + ($range % 2 === 0);
|
||||
$end = $start + $range - 1;
|
||||
$middle = (int)floor($range/2);
|
||||
$start = $page - $middle + ($range % 2 === 0);
|
||||
$end = $start + $range - 1;
|
||||
|
||||
if ($start <= 0) {
|
||||
$end = $range;
|
||||
$start = 1;
|
||||
}
|
||||
if ($start <= 0) {
|
||||
$end = $range;
|
||||
$start = 1;
|
||||
}
|
||||
|
||||
if ($end > $pages) {
|
||||
$start = $pages - $range + 1;
|
||||
$end = $pages;
|
||||
}
|
||||
if ($end > $pages) {
|
||||
$start = $pages - $range + 1;
|
||||
$end = $pages;
|
||||
}
|
||||
|
||||
return range($start, $end);
|
||||
}
|
||||
return range($start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first page of the created range
|
||||
*
|
||||
* @param int $range
|
||||
* @return int
|
||||
*/
|
||||
public function rangeStart(int $range = 5): int
|
||||
{
|
||||
return $this->range($range)[0];
|
||||
}
|
||||
/**
|
||||
* Returns the first page of the created range
|
||||
*
|
||||
* @param int $range
|
||||
* @return int
|
||||
*/
|
||||
public function rangeStart(int $range = 5): int
|
||||
{
|
||||
return $this->range($range)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last page of the created range
|
||||
*
|
||||
* @param int $range
|
||||
* @return int
|
||||
*/
|
||||
public function rangeEnd(int $range = 5): int
|
||||
{
|
||||
$range = $this->range($range);
|
||||
return array_pop($range);
|
||||
}
|
||||
/**
|
||||
* Returns the last page of the created range
|
||||
*
|
||||
* @param int $range
|
||||
* @return int
|
||||
*/
|
||||
public function rangeEnd(int $range = 5): int
|
||||
{
|
||||
$range = $this->range($range);
|
||||
return array_pop($range);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the properties limit, total and page
|
||||
* and validates that the properties match
|
||||
*
|
||||
* @param array $props Array with keys limit, total and/or page
|
||||
* @return $this
|
||||
*/
|
||||
protected function setProperties(array $props)
|
||||
{
|
||||
$this->baseSetProperties($props);
|
||||
/**
|
||||
* Sets the properties limit, total and page
|
||||
* and validates that the properties match
|
||||
*
|
||||
* @param array $props Array with keys limit, total and/or page
|
||||
* @return $this
|
||||
*/
|
||||
protected function setProperties(array $props)
|
||||
{
|
||||
$this->baseSetProperties($props);
|
||||
|
||||
// ensure that page is set to something, otherwise
|
||||
// generate "default page" based on other params
|
||||
if ($this->page === null) {
|
||||
$this->page = $this->firstPage();
|
||||
}
|
||||
// ensure that page is set to something, otherwise
|
||||
// generate "default page" based on other params
|
||||
if ($this->page === null) {
|
||||
$this->page = $this->firstPage();
|
||||
}
|
||||
|
||||
// allow a page value of 1 even if there are no pages;
|
||||
// otherwise the exception will get thrown for this pretty common case
|
||||
$min = $this->firstPage();
|
||||
$max = $this->pages();
|
||||
if ($this->page === 1 && $max === 0) {
|
||||
$this->page = 0;
|
||||
}
|
||||
// allow a page value of 1 even if there are no pages;
|
||||
// otherwise the exception will get thrown for this pretty common case
|
||||
$min = $this->firstPage();
|
||||
$max = $this->pages();
|
||||
if ($this->page === 1 && $max === 0) {
|
||||
$this->page = 0;
|
||||
}
|
||||
|
||||
// validate page based on all params if validation is enabled,
|
||||
// otherwise limit the page number to the bounds
|
||||
if ($this->page < $min || $this->page > $max) {
|
||||
if (static::$validate === true) {
|
||||
throw new ErrorPageException('Pagination page ' . $this->page . ' does not exist, expected ' . $min . '-' . $max);
|
||||
} else {
|
||||
$this->page = max(min($this->page, $max), $min);
|
||||
}
|
||||
}
|
||||
// validate page based on all params if validation is enabled,
|
||||
// otherwise limit the page number to the bounds
|
||||
if ($this->page < $min || $this->page > $max) {
|
||||
if (static::$validate === true) {
|
||||
throw new ErrorPageException('Pagination page ' . $this->page . ' does not exist, expected ' . $min . '-' . $max);
|
||||
} else {
|
||||
$this->page = max(min($this->page, $max), $min);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of items per page
|
||||
*
|
||||
* @param int $limit
|
||||
* @return $this
|
||||
*/
|
||||
protected function setLimit(int $limit = 20)
|
||||
{
|
||||
if ($limit < 1) {
|
||||
throw new Exception('Invalid pagination limit: ' . $limit);
|
||||
}
|
||||
/**
|
||||
* Sets the number of items per page
|
||||
*
|
||||
* @param int $limit
|
||||
* @return $this
|
||||
*/
|
||||
protected function setLimit(int $limit = 20)
|
||||
{
|
||||
if ($limit < 1) {
|
||||
throw new Exception('Invalid pagination limit: ' . $limit);
|
||||
}
|
||||
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total number of items
|
||||
*
|
||||
* @param int $total
|
||||
* @return $this
|
||||
*/
|
||||
protected function setTotal(int $total = 0)
|
||||
{
|
||||
if ($total < 0) {
|
||||
throw new Exception('Invalid total number of items: ' . $total);
|
||||
}
|
||||
/**
|
||||
* Sets the total number of items
|
||||
*
|
||||
* @param int $total
|
||||
* @return $this
|
||||
*/
|
||||
protected function setTotal(int $total = 0)
|
||||
{
|
||||
if ($total < 0) {
|
||||
throw new Exception('Invalid total number of items: ' . $total);
|
||||
}
|
||||
|
||||
$this->total = $total;
|
||||
return $this;
|
||||
}
|
||||
$this->total = $total;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current page
|
||||
*
|
||||
* @param int|string|null $page Int or int in string form;
|
||||
* automatically determined if null
|
||||
* @return $this
|
||||
*/
|
||||
protected function setPage($page = null)
|
||||
{
|
||||
// if $page is null, it is set to a default in the setProperties() method
|
||||
if ($page !== null) {
|
||||
if (is_numeric($page) !== true || $page < 0) {
|
||||
throw new Exception('Invalid page number: ' . $page);
|
||||
}
|
||||
/**
|
||||
* Sets the current page
|
||||
*
|
||||
* @param int|string|null $page Int or int in string form;
|
||||
* automatically determined if null
|
||||
* @return $this
|
||||
*/
|
||||
protected function setPage($page = null)
|
||||
{
|
||||
// if $page is null, it is set to a default in the setProperties() method
|
||||
if ($page !== null) {
|
||||
if (is_numeric($page) !== true || $page < 0) {
|
||||
throw new Exception('Invalid page number: ' . $page);
|
||||
}
|
||||
|
||||
$this->page = (int)$page;
|
||||
}
|
||||
$this->page = (int)$page;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all properties
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'page' => $this->page(),
|
||||
'firstPage' => $this->firstPage(),
|
||||
'lastPage' => $this->lastPage(),
|
||||
'pages' => $this->pages(),
|
||||
'offset' => $this->offset(),
|
||||
'limit' => $this->limit(),
|
||||
'total' => $this->total(),
|
||||
'start' => $this->start(),
|
||||
'end' => $this->end(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Returns an array with all properties
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'page' => $this->page(),
|
||||
'firstPage' => $this->firstPage(),
|
||||
'lastPage' => $this->lastPage(),
|
||||
'pages' => $this->pages(),
|
||||
'offset' => $this->offset(),
|
||||
'limit' => $this->limit(),
|
||||
'total' => $this->total(),
|
||||
'start' => $this->start(),
|
||||
'end' => $this->end(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -16,136 +16,136 @@ use ReflectionMethod;
|
||||
*/
|
||||
trait Properties
|
||||
{
|
||||
protected $propertyData = [];
|
||||
protected $propertyData = [];
|
||||
|
||||
/**
|
||||
* Creates an instance with the same
|
||||
* initial properties.
|
||||
*
|
||||
* @param array $props
|
||||
* @return static
|
||||
*/
|
||||
public function clone(array $props = [])
|
||||
{
|
||||
return new static(array_replace_recursive($this->propertyData, $props));
|
||||
}
|
||||
/**
|
||||
* Creates an instance with the same
|
||||
* initial properties.
|
||||
*
|
||||
* @param array $props
|
||||
* @return static
|
||||
*/
|
||||
public function clone(array $props = [])
|
||||
{
|
||||
return new static(array_replace_recursive($this->propertyData, $props));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a clone and fetches all
|
||||
* lazy-loaded getters to get a full copy
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function hardcopy()
|
||||
{
|
||||
$clone = $this->clone();
|
||||
$clone->propertiesToArray();
|
||||
return $clone;
|
||||
}
|
||||
/**
|
||||
* Creates a clone and fetches all
|
||||
* lazy-loaded getters to get a full copy
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function hardcopy()
|
||||
{
|
||||
$clone = $this->clone();
|
||||
$clone->propertiesToArray();
|
||||
return $clone;
|
||||
}
|
||||
|
||||
protected function isRequiredProperty(string $name): bool
|
||||
{
|
||||
$method = new ReflectionMethod($this, 'set' . $name);
|
||||
return $method->getNumberOfRequiredParameters() > 0;
|
||||
}
|
||||
protected function isRequiredProperty(string $name): bool
|
||||
{
|
||||
$method = new ReflectionMethod($this, 'set' . $name);
|
||||
return $method->getNumberOfRequiredParameters() > 0;
|
||||
}
|
||||
|
||||
protected function propertiesToArray()
|
||||
{
|
||||
$array = [];
|
||||
protected function propertiesToArray()
|
||||
{
|
||||
$array = [];
|
||||
|
||||
foreach (get_object_vars($this) as $name => $default) {
|
||||
if ($name === 'propertyData') {
|
||||
continue;
|
||||
}
|
||||
foreach (get_object_vars($this) as $name => $default) {
|
||||
if ($name === 'propertyData') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method_exists($this, 'convert' . $name . 'ToArray') === true) {
|
||||
$array[$name] = $this->{'convert' . $name . 'ToArray'}();
|
||||
continue;
|
||||
}
|
||||
if (method_exists($this, 'convert' . $name . 'ToArray') === true) {
|
||||
$array[$name] = $this->{'convert' . $name . 'ToArray'}();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method_exists($this, $name) === true) {
|
||||
$method = new ReflectionMethod($this, $name);
|
||||
if (method_exists($this, $name) === true) {
|
||||
$method = new ReflectionMethod($this, $name);
|
||||
|
||||
if ($method->isPublic() === true) {
|
||||
$value = $this->$name();
|
||||
if ($method->isPublic() === true) {
|
||||
$value = $this->$name();
|
||||
|
||||
if (is_object($value) === false) {
|
||||
$array[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_object($value) === false) {
|
||||
$array[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ksort($array);
|
||||
ksort($array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
protected function setOptionalProperties(array $props, array $optional)
|
||||
{
|
||||
$this->propertyData = array_merge($this->propertyData, $props);
|
||||
protected function setOptionalProperties(array $props, array $optional)
|
||||
{
|
||||
$this->propertyData = array_merge($this->propertyData, $props);
|
||||
|
||||
foreach ($optional as $propertyName) {
|
||||
if (isset($props[$propertyName]) === true) {
|
||||
$this->{'set' . $propertyName}($props[$propertyName]);
|
||||
} else {
|
||||
$this->{'set' . $propertyName}();
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($optional as $propertyName) {
|
||||
if (isset($props[$propertyName]) === true) {
|
||||
$this->{'set' . $propertyName}($props[$propertyName]);
|
||||
} else {
|
||||
$this->{'set' . $propertyName}();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function setProperties($props, array $keys = null)
|
||||
{
|
||||
foreach (get_object_vars($this) as $name => $default) {
|
||||
if ($name === 'propertyData') {
|
||||
continue;
|
||||
}
|
||||
protected function setProperties($props, array $keys = null)
|
||||
{
|
||||
foreach (get_object_vars($this) as $name => $default) {
|
||||
if ($name === 'propertyData') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->setProperty($name, $props[$name] ?? $default);
|
||||
}
|
||||
$this->setProperty($name, $props[$name] ?? $default);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function setProperty($name, $value, $required = null)
|
||||
{
|
||||
// use a setter if it exists
|
||||
if (method_exists($this, 'set' . $name) === false) {
|
||||
return $this;
|
||||
}
|
||||
protected function setProperty($name, $value, $required = null)
|
||||
{
|
||||
// use a setter if it exists
|
||||
if (method_exists($this, 'set' . $name) === false) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// fetch the default value from the property
|
||||
$value ??= $this->$name ?? null;
|
||||
// fetch the default value from the property
|
||||
$value ??= $this->$name ?? null;
|
||||
|
||||
// store all original properties, to be able to clone them later
|
||||
$this->propertyData[$name] = $value;
|
||||
// store all original properties, to be able to clone them later
|
||||
$this->propertyData[$name] = $value;
|
||||
|
||||
// handle empty values
|
||||
if ($value === null) {
|
||||
// handle empty values
|
||||
if ($value === null) {
|
||||
|
||||
// replace null with a default value, if a default handler exists
|
||||
if (method_exists($this, 'default' . $name) === true) {
|
||||
$value = $this->{'default' . $name}();
|
||||
}
|
||||
// replace null with a default value, if a default handler exists
|
||||
if (method_exists($this, 'default' . $name) === true) {
|
||||
$value = $this->{'default' . $name}();
|
||||
}
|
||||
|
||||
// check for required properties
|
||||
if ($value === null && ($required ?? $this->isRequiredProperty($name)) === true) {
|
||||
throw new Exception(sprintf('The property "%s" is required', $name));
|
||||
}
|
||||
}
|
||||
// check for required properties
|
||||
if ($value === null && ($required ?? $this->isRequiredProperty($name)) === true) {
|
||||
throw new Exception(sprintf('The property "%s" is required', $name));
|
||||
}
|
||||
}
|
||||
|
||||
// call the setter with the final value
|
||||
return $this->{'set' . $name}($value);
|
||||
}
|
||||
// call the setter with the final value
|
||||
return $this->{'set' . $name}($value);
|
||||
}
|
||||
|
||||
protected function setRequiredProperties(array $props, array $required)
|
||||
{
|
||||
foreach ($required as $propertyName) {
|
||||
if (isset($props[$propertyName]) !== true) {
|
||||
throw new Exception(sprintf('The property "%s" is required', $propertyName));
|
||||
}
|
||||
protected function setRequiredProperties(array $props, array $required)
|
||||
{
|
||||
foreach ($required as $propertyName) {
|
||||
if (isset($props[$propertyName]) !== true) {
|
||||
throw new Exception(sprintf('The property "%s" is required', $propertyName));
|
||||
}
|
||||
|
||||
$this->{'set' . $propertyName}($props[$propertyName]);
|
||||
}
|
||||
}
|
||||
$this->{'set' . $propertyName}($props[$propertyName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,227 +18,227 @@ use Kirby\Exception\InvalidArgumentException;
|
||||
*/
|
||||
class Query
|
||||
{
|
||||
public const PARTS = '!\.|(\(([^()]+|(?1))*+\))(*SKIP)(*FAIL)!'; // split by dot, but not inside (nested) parens
|
||||
public const PARAMETERS = '!,|' . self::SKIP . '!'; // split by comma, but not inside skip groups
|
||||
public const PARTS = '!\.|(\(([^()]+|(?1))*+\))(*SKIP)(*FAIL)!'; // split by dot, but not inside (nested) parens
|
||||
public const PARAMETERS = '!,|' . self::SKIP . '!'; // split by comma, but not inside skip groups
|
||||
|
||||
public const NO_PNTH = '\([^(]+\)(*SKIP)(*FAIL)';
|
||||
public const NO_SQBR = '\[[^]]+\](*SKIP)(*FAIL)';
|
||||
public const NO_DLQU = '\"(?:[^"\\\\]|\\\\.)*\"(*SKIP)(*FAIL)'; // allow \" escaping inside string
|
||||
public const NO_SLQU = '\'(?:[^\'\\\\]|\\\\.)*\'(*SKIP)(*FAIL)'; // allow \' escaping inside string
|
||||
public const SKIP = self::NO_PNTH . '|' . self::NO_SQBR . '|' .
|
||||
self::NO_DLQU . '|' . self::NO_SLQU;
|
||||
public const NO_PNTH = '\([^(]+\)(*SKIP)(*FAIL)';
|
||||
public const NO_SQBR = '\[[^]]+\](*SKIP)(*FAIL)';
|
||||
public const NO_DLQU = '\"(?:[^"\\\\]|\\\\.)*\"(*SKIP)(*FAIL)'; // allow \" escaping inside string
|
||||
public const NO_SLQU = '\'(?:[^\'\\\\]|\\\\.)*\'(*SKIP)(*FAIL)'; // allow \' escaping inside string
|
||||
public const SKIP = self::NO_PNTH . '|' . self::NO_SQBR . '|' .
|
||||
self::NO_DLQU . '|' . self::NO_SLQU;
|
||||
|
||||
/**
|
||||
* The query string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $query;
|
||||
/**
|
||||
* The query string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* Queryable data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
/**
|
||||
* Queryable data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Creates a new Query object
|
||||
*
|
||||
* @param string|null $query
|
||||
* @param array|object $data
|
||||
*/
|
||||
public function __construct(?string $query = null, $data = [])
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->data = $data;
|
||||
}
|
||||
/**
|
||||
* Creates a new Query object
|
||||
*
|
||||
* @param string|null $query
|
||||
* @param array|object $data
|
||||
*/
|
||||
public function __construct(?string $query = null, $data = [])
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query result if anything
|
||||
* can be found, otherwise returns null
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function result()
|
||||
{
|
||||
if (empty($this->query) === true) {
|
||||
return $this->data;
|
||||
}
|
||||
/**
|
||||
* Returns the query result if anything
|
||||
* can be found, otherwise returns null
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function result()
|
||||
{
|
||||
if (empty($this->query) === true) {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
return $this->resolve($this->query);
|
||||
}
|
||||
return $this->resolve($this->query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the query if anything
|
||||
* can be found, otherwise returns null
|
||||
*
|
||||
* @param string $query
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Kirby\Exception\BadMethodCallException If an invalid method is accessed by the query
|
||||
*/
|
||||
protected function resolve(string $query)
|
||||
{
|
||||
// direct key access in arrays
|
||||
if (is_array($this->data) === true && array_key_exists($query, $this->data) === true) {
|
||||
return $this->data[$query];
|
||||
}
|
||||
/**
|
||||
* Resolves the query if anything
|
||||
* can be found, otherwise returns null
|
||||
*
|
||||
* @param string $query
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Kirby\Exception\BadMethodCallException If an invalid method is accessed by the query
|
||||
*/
|
||||
protected function resolve(string $query)
|
||||
{
|
||||
// direct key access in arrays
|
||||
if (is_array($this->data) === true && array_key_exists($query, $this->data) === true) {
|
||||
return $this->data[$query];
|
||||
}
|
||||
|
||||
$parts = $this->parts($query);
|
||||
$data = $this->data;
|
||||
$value = null;
|
||||
$parts = $this->parts($query);
|
||||
$data = $this->data;
|
||||
$value = null;
|
||||
|
||||
foreach ($parts as $part) {
|
||||
$info = $this->part($part);
|
||||
$method = $info['method'];
|
||||
$args = $info['args'];
|
||||
foreach ($parts as $part) {
|
||||
$info = $this->part($part);
|
||||
$method = $info['method'];
|
||||
$args = $info['args'];
|
||||
|
||||
if (is_array($data)) {
|
||||
if (array_key_exists($method, $data) === true) {
|
||||
$value = $data[$method];
|
||||
if (is_array($data)) {
|
||||
if (array_key_exists($method, $data) === true) {
|
||||
$value = $data[$method];
|
||||
|
||||
if (is_a($value, 'Closure') === true) {
|
||||
$value = $value(...$args);
|
||||
} elseif ($args !== []) {
|
||||
throw new InvalidArgumentException('Cannot access array element ' . $method . ' with arguments');
|
||||
}
|
||||
} else {
|
||||
static::accessError($data, $method, 'property');
|
||||
}
|
||||
} elseif (is_object($data)) {
|
||||
if (
|
||||
method_exists($data, $method) === true ||
|
||||
method_exists($data, '__call') === true
|
||||
) {
|
||||
$value = $data->$method(...$args);
|
||||
} elseif (
|
||||
$args === [] && (
|
||||
property_exists($data, $method) === true ||
|
||||
method_exists($data, '__get') === true
|
||||
)
|
||||
) {
|
||||
$value = $data->$method;
|
||||
} else {
|
||||
$label = ($args === []) ? 'method/property' : 'method';
|
||||
static::accessError($data, $method, $label);
|
||||
}
|
||||
} else {
|
||||
// further parts on a scalar/null value
|
||||
static::accessError($data, $method, 'method/property');
|
||||
}
|
||||
if (is_a($value, 'Closure') === true) {
|
||||
$value = $value(...$args);
|
||||
} elseif ($args !== []) {
|
||||
throw new InvalidArgumentException('Cannot access array element ' . $method . ' with arguments');
|
||||
}
|
||||
} else {
|
||||
static::accessError($data, $method, 'property');
|
||||
}
|
||||
} elseif (is_object($data)) {
|
||||
if (
|
||||
method_exists($data, $method) === true ||
|
||||
method_exists($data, '__call') === true
|
||||
) {
|
||||
$value = $data->$method(...$args);
|
||||
} elseif (
|
||||
$args === [] && (
|
||||
property_exists($data, $method) === true ||
|
||||
method_exists($data, '__get') === true
|
||||
)
|
||||
) {
|
||||
$value = $data->$method;
|
||||
} else {
|
||||
$label = ($args === []) ? 'method/property' : 'method';
|
||||
static::accessError($data, $method, $label);
|
||||
}
|
||||
} else {
|
||||
// further parts on a scalar/null value
|
||||
static::accessError($data, $method, 'method/property');
|
||||
}
|
||||
|
||||
// continue with the current value for the next part
|
||||
$data = $value;
|
||||
}
|
||||
// continue with the current value for the next part
|
||||
$data = $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks the query string down into its components
|
||||
*
|
||||
* @param string $query
|
||||
* @return array
|
||||
*/
|
||||
protected function parts(string $query): array
|
||||
{
|
||||
return preg_split(self::PARTS, trim($query), -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
/**
|
||||
* Breaks the query string down into its components
|
||||
*
|
||||
* @param string $query
|
||||
* @return array
|
||||
*/
|
||||
protected function parts(string $query): array
|
||||
{
|
||||
return preg_split(self::PARTS, trim($query), -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes each part of the query string and
|
||||
* extracts methods and method arguments
|
||||
*
|
||||
* @param string $part
|
||||
* @return array
|
||||
*/
|
||||
protected function part(string $part): array
|
||||
{
|
||||
if (Str::endsWith($part, ')') === true) {
|
||||
$method = Str::before($part, '(');
|
||||
/**
|
||||
* Analyzes each part of the query string and
|
||||
* extracts methods and method arguments
|
||||
*
|
||||
* @param string $part
|
||||
* @return array
|
||||
*/
|
||||
protected function part(string $part): array
|
||||
{
|
||||
if (Str::endsWith($part, ')') === true) {
|
||||
$method = Str::before($part, '(');
|
||||
|
||||
// the args are everything inside the *outer* parentheses
|
||||
$args = Str::substr($part, Str::position($part, '(') + 1, -1);
|
||||
$args = preg_split(self::PARAMETERS, $args);
|
||||
$args = array_map('self::parameter', $args);
|
||||
// the args are everything inside the *outer* parentheses
|
||||
$args = Str::substr($part, Str::position($part, '(') + 1, -1);
|
||||
$args = preg_split(self::PARAMETERS, $args);
|
||||
$args = array_map('self::parameter', $args);
|
||||
|
||||
return compact('method', 'args');
|
||||
} else {
|
||||
return [
|
||||
'method' => $part,
|
||||
'args' => []
|
||||
];
|
||||
}
|
||||
}
|
||||
return compact('method', 'args');
|
||||
} else {
|
||||
return [
|
||||
'method' => $part,
|
||||
'args' => []
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a parameter of a query to
|
||||
* its proper native PHP type
|
||||
*
|
||||
* @param string $arg
|
||||
* @return mixed
|
||||
*/
|
||||
protected function parameter(string $arg)
|
||||
{
|
||||
$arg = trim($arg);
|
||||
/**
|
||||
* Converts a parameter of a query to
|
||||
* its proper native PHP type
|
||||
*
|
||||
* @param string $arg
|
||||
* @return mixed
|
||||
*/
|
||||
protected function parameter(string $arg)
|
||||
{
|
||||
$arg = trim($arg);
|
||||
|
||||
// string with double quotes
|
||||
if (substr($arg, 0, 1) === '"' && substr($arg, -1) === '"') {
|
||||
return str_replace('\"', '"', substr($arg, 1, -1));
|
||||
}
|
||||
// string with double quotes
|
||||
if (substr($arg, 0, 1) === '"' && substr($arg, -1) === '"') {
|
||||
return str_replace('\"', '"', substr($arg, 1, -1));
|
||||
}
|
||||
|
||||
// string with single quotes
|
||||
if (substr($arg, 0, 1) === "'" && substr($arg, -1) === "'") {
|
||||
return str_replace("\'", "'", substr($arg, 1, -1));
|
||||
}
|
||||
// string with single quotes
|
||||
if (substr($arg, 0, 1) === "'" && substr($arg, -1) === "'") {
|
||||
return str_replace("\'", "'", substr($arg, 1, -1));
|
||||
}
|
||||
|
||||
// boolean or null
|
||||
switch ($arg) {
|
||||
case 'null':
|
||||
return null;
|
||||
case 'false':
|
||||
return false;
|
||||
case 'true':
|
||||
return true;
|
||||
}
|
||||
// boolean or null
|
||||
switch ($arg) {
|
||||
case 'null':
|
||||
return null;
|
||||
case 'false':
|
||||
return false;
|
||||
case 'true':
|
||||
return true;
|
||||
}
|
||||
|
||||
// numeric
|
||||
if (is_numeric($arg) === true) {
|
||||
return (float)$arg;
|
||||
}
|
||||
// numeric
|
||||
if (is_numeric($arg) === true) {
|
||||
return (float)$arg;
|
||||
}
|
||||
|
||||
// array: split and recursive sanitizing
|
||||
if (substr($arg, 0, 1) === '[' && substr($arg, -1) === ']') {
|
||||
$arg = substr($arg, 1, -1);
|
||||
$arg = preg_split(self::PARAMETERS, $arg);
|
||||
return array_map('self::parameter', $arg);
|
||||
}
|
||||
// array: split and recursive sanitizing
|
||||
if (substr($arg, 0, 1) === '[' && substr($arg, -1) === ']') {
|
||||
$arg = substr($arg, 1, -1);
|
||||
$arg = preg_split(self::PARAMETERS, $arg);
|
||||
return array_map('self::parameter', $arg);
|
||||
}
|
||||
|
||||
// resolve parameter for objects and methods itself
|
||||
return $this->resolve($arg);
|
||||
}
|
||||
// resolve parameter for objects and methods itself
|
||||
return $this->resolve($arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception for an access to an invalid method
|
||||
*
|
||||
* @param mixed $data Variable on which the access was tried
|
||||
* @param string $name Name of the method/property that was accessed
|
||||
* @param string $label Type of the name (`method`, `property` or `method/property`)
|
||||
* @return void
|
||||
*
|
||||
* @throws \Kirby\Exception\BadMethodCallException
|
||||
*/
|
||||
protected static function accessError($data, string $name, string $label): void
|
||||
{
|
||||
$type = strtolower(gettype($data));
|
||||
if ($type === 'double') {
|
||||
$type = 'float';
|
||||
}
|
||||
/**
|
||||
* Throws an exception for an access to an invalid method
|
||||
*
|
||||
* @param mixed $data Variable on which the access was tried
|
||||
* @param string $name Name of the method/property that was accessed
|
||||
* @param string $label Type of the name (`method`, `property` or `method/property`)
|
||||
* @return void
|
||||
*
|
||||
* @throws \Kirby\Exception\BadMethodCallException
|
||||
*/
|
||||
protected static function accessError($data, string $name, string $label): void
|
||||
{
|
||||
$type = strtolower(gettype($data));
|
||||
if ($type === 'double') {
|
||||
$type = 'float';
|
||||
}
|
||||
|
||||
$nonExisting = in_array($type, ['array', 'object']) ? 'non-existing ' : '';
|
||||
$nonExisting = in_array($type, ['array', 'object']) ? 'non-existing ' : '';
|
||||
|
||||
$error = 'Access to ' . $nonExisting . $label . ' ' . $name . ' on ' . $type;
|
||||
throw new BadMethodCallException($error);
|
||||
}
|
||||
$error = 'Access to ' . $nonExisting . $label . ' ' . $name . ' on ' . $type;
|
||||
throw new BadMethodCallException($error);
|
||||
}
|
||||
}
|
||||
|
@@ -15,59 +15,59 @@ namespace Kirby\Toolkit;
|
||||
*/
|
||||
class Silo
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $data = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $data = [];
|
||||
|
||||
/**
|
||||
* Setter for new data.
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
public static function set($key, $value = null): array
|
||||
{
|
||||
if (is_array($key) === true) {
|
||||
return static::$data = array_merge(static::$data, $key);
|
||||
} else {
|
||||
static::$data[$key] = $value;
|
||||
return static::$data;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Setter for new data.
|
||||
*
|
||||
* @param string|array $key
|
||||
* @param mixed $value
|
||||
* @return array
|
||||
*/
|
||||
public static function set($key, $value = null): array
|
||||
{
|
||||
if (is_array($key) === true) {
|
||||
return static::$data = array_merge(static::$data, $key);
|
||||
} else {
|
||||
static::$data[$key] = $value;
|
||||
return static::$data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($key = null, $default = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return static::$data;
|
||||
}
|
||||
/**
|
||||
* @param string|array $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($key = null, $default = null)
|
||||
{
|
||||
if ($key === null) {
|
||||
return static::$data;
|
||||
}
|
||||
|
||||
return A::get(static::$data, $key, $default);
|
||||
}
|
||||
return A::get(static::$data, $key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the data array
|
||||
*
|
||||
* @param string|null $key
|
||||
* @return array
|
||||
*/
|
||||
public static function remove(string $key = null): array
|
||||
{
|
||||
// reset the entire array
|
||||
if ($key === null) {
|
||||
return static::$data = [];
|
||||
}
|
||||
/**
|
||||
* Removes an item from the data array
|
||||
*
|
||||
* @param string|null $key
|
||||
* @return array
|
||||
*/
|
||||
public static function remove(string $key = null): array
|
||||
{
|
||||
// reset the entire array
|
||||
if ($key === null) {
|
||||
return static::$data = [];
|
||||
}
|
||||
|
||||
// unset a single key
|
||||
unset(static::$data[$key]);
|
||||
// unset a single key
|
||||
unset(static::$data[$key]);
|
||||
|
||||
// return the array without the removed key
|
||||
return static::$data;
|
||||
}
|
||||
// return the array without the removed key
|
||||
return static::$data;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -16,36 +16,36 @@ use Throwable;
|
||||
*/
|
||||
class Tpl
|
||||
{
|
||||
/**
|
||||
* Renders the template
|
||||
*
|
||||
* @param string|null $file
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function load(?string $file = null, array $data = []): string
|
||||
{
|
||||
if ($file === null || is_file($file) === false) {
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* Renders the template
|
||||
*
|
||||
* @param string|null $file
|
||||
* @param array $data
|
||||
* @return string
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function load(?string $file = null, array $data = []): string
|
||||
{
|
||||
if ($file === null || is_file($file) === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
ob_start();
|
||||
|
||||
$exception = null;
|
||||
try {
|
||||
F::load($file, null, $data);
|
||||
} catch (Throwable $e) {
|
||||
$exception = $e;
|
||||
}
|
||||
$exception = null;
|
||||
try {
|
||||
F::load($file, null, $data);
|
||||
} catch (Throwable $e) {
|
||||
$exception = $e;
|
||||
}
|
||||
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
if ($exception === null) {
|
||||
return $content;
|
||||
}
|
||||
if ($exception === null) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -17,121 +17,121 @@ use Throwable;
|
||||
*/
|
||||
class View
|
||||
{
|
||||
/**
|
||||
* The absolute path to the view file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $file;
|
||||
/**
|
||||
* The absolute path to the view file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $file;
|
||||
|
||||
/**
|
||||
* The view data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
/**
|
||||
* The view data
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* Creates a new view object
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(string $file, array $data = [])
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->data = $data;
|
||||
}
|
||||
/**
|
||||
* Creates a new view object
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(string $file, array $data = [])
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view's data array
|
||||
* without globals.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
/**
|
||||
* Returns the view's data array
|
||||
* without globals.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function data(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the template file exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
return is_file($this->file()) === true;
|
||||
}
|
||||
/**
|
||||
* Checks if the template file exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(): bool
|
||||
{
|
||||
return is_file($this->file()) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view file
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function file()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
/**
|
||||
* Returns the view file
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function file()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an error message for the missing view exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function missingViewMessage(): string
|
||||
{
|
||||
return 'The view does not exist: ' . $this->file();
|
||||
}
|
||||
/**
|
||||
* Creates an error message for the missing view exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function missingViewMessage(): string
|
||||
{
|
||||
return 'The view does not exist: ' . $this->file();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the view
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
if ($this->exists() === false) {
|
||||
throw new Exception($this->missingViewMessage());
|
||||
}
|
||||
/**
|
||||
* Renders the view
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
if ($this->exists() === false) {
|
||||
throw new Exception($this->missingViewMessage());
|
||||
}
|
||||
|
||||
ob_start();
|
||||
ob_start();
|
||||
|
||||
$exception = null;
|
||||
try {
|
||||
F::load($this->file(), null, $this->data());
|
||||
} catch (Throwable $e) {
|
||||
$exception = $e;
|
||||
}
|
||||
$exception = null;
|
||||
try {
|
||||
F::load($this->file(), null, $this->data());
|
||||
} catch (Throwable $e) {
|
||||
$exception = $e;
|
||||
}
|
||||
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
if ($exception === null) {
|
||||
return $content;
|
||||
}
|
||||
if ($exception === null) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for View::render()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
/**
|
||||
* Alias for View::render()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic string converter to enable
|
||||
* converting view objects to string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
/**
|
||||
* Magic string converter to enable
|
||||
* converting view objects to string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->render();
|
||||
}
|
||||
}
|
||||
|
@@ -15,420 +15,420 @@ use SimpleXMLElement;
|
||||
*/
|
||||
class Xml
|
||||
{
|
||||
/**
|
||||
* HTML to XML conversion table for entities
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $entities = [
|
||||
' ' => ' ', '¡' => '¡', '¢' => '¢', '£' => '£', '¤' => '¤', '¥' => '¥', '¦' => '¦', '§' => '§',
|
||||
'¨' => '¨', '©' => '©', 'ª' => 'ª', '«' => '«', '¬' => '¬', '­' => '­', '®' => '®', '¯' => '¯',
|
||||
'°' => '°', '±' => '±', '²' => '²', '³' => '³', '´' => '´', 'µ' => 'µ', '¶' => '¶', '·' => '·',
|
||||
'¸' => '¸', '¹' => '¹', 'º' => 'º', '»' => '»', '¼' => '¼', '½' => '½', '¾' => '¾', '¿' => '¿',
|
||||
'À' => 'À', 'Á' => 'Á', 'Â' => 'Â', 'Ã' => 'Ã', 'Ä' => 'Ä', 'Å' => 'Å', 'Æ' => 'Æ', 'Ç' => 'Ç',
|
||||
'È' => 'È', 'É' => 'É', 'Ê' => 'Ê', 'Ë' => 'Ë', 'Ì' => 'Ì', 'Í' => 'Í', 'Î' => 'Î', 'Ï' => 'Ï',
|
||||
'Ð' => 'Ð', 'Ñ' => 'Ñ', 'Ò' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô', 'Õ' => 'Õ', 'Ö' => 'Ö', '×' => '×',
|
||||
'Ø' => 'Ø', 'Ù' => 'Ù', 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', 'Ý' => 'Ý', 'Þ' => 'Þ', 'ß' => 'ß',
|
||||
'à' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã', 'ä' => 'ä', 'å' => 'å', 'æ' => 'æ', 'ç' => 'ç',
|
||||
'è' => 'è', 'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'í' => 'í', 'î' => 'î', 'ï' => 'ï',
|
||||
'ð' => 'ð', 'ñ' => 'ñ', 'ò' => 'ò', 'ó' => 'ó', 'ô' => 'ô', 'õ' => 'õ', 'ö' => 'ö', '÷' => '÷',
|
||||
'ø' => 'ø', 'ù' => 'ù', 'ú' => 'ú', 'û' => 'û', 'ü' => 'ü', 'ý' => 'ý', 'þ' => 'þ', 'ÿ' => 'ÿ',
|
||||
'ƒ' => 'ƒ', 'Α' => 'Α', 'Β' => 'Β', 'Γ' => 'Γ', 'Δ' => 'Δ', 'Ε' => 'Ε', 'Ζ' => 'Ζ', 'Η' => 'Η',
|
||||
'Θ' => 'Θ', 'Ι' => 'Ι', 'Κ' => 'Κ', 'Λ' => 'Λ', 'Μ' => 'Μ', 'Ν' => 'Ν', 'Ξ' => 'Ξ', 'Ο' => 'Ο',
|
||||
'Π' => 'Π', 'Ρ' => 'Ρ', 'Σ' => 'Σ', 'Τ' => 'Τ', 'Υ' => 'Υ', 'Φ' => 'Φ', 'Χ' => 'Χ', 'Ψ' => 'Ψ',
|
||||
'Ω' => 'Ω', 'α' => 'α', 'β' => 'β', 'γ' => 'γ', 'δ' => 'δ', 'ε' => 'ε', 'ζ' => 'ζ', 'η' => 'η',
|
||||
'θ' => 'θ', 'ι' => 'ι', 'κ' => 'κ', 'λ' => 'λ', 'μ' => 'μ', 'ν' => 'ν', 'ξ' => 'ξ', 'ο' => 'ο',
|
||||
'π' => 'π', 'ρ' => 'ρ', 'ς' => 'ς', 'σ' => 'σ', 'τ' => 'τ', 'υ' => 'υ', 'φ' => 'φ', 'χ' => 'χ',
|
||||
'ψ' => 'ψ', 'ω' => 'ω', 'ϑ' => 'ϑ', 'ϒ' => 'ϒ', 'ϖ' => 'ϖ', '•' => '•', '…' => '…', '′' => '′',
|
||||
'″' => '″', '‾' => '‾', '⁄' => '⁄', '℘' => '℘', 'ℑ' => 'ℑ', 'ℜ' => 'ℜ', '™' => '™', 'ℵ' => 'ℵ',
|
||||
'←' => '←', '↑' => '↑', '→' => '→', '↓' => '↓', '↔' => '↔', '↵' => '↵', '⇐' => '⇐', '⇑' => '⇑',
|
||||
'⇒' => '⇒', '⇓' => '⇓', '⇔' => '⇔', '∀' => '∀', '∂' => '∂', '∃' => '∃', '∅' => '∅', '∇' => '∇',
|
||||
'∈' => '∈', '∉' => '∉', '∋' => '∋', '∏' => '∏', '∑' => '∑', '−' => '−', '∗' => '∗', '√' => '√',
|
||||
'∝' => '∝', '∞' => '∞', '∠' => '∠', '∧' => '∧', '∨' => '∨', '∩' => '∩', '∪' => '∪', '∫' => '∫',
|
||||
'∴' => '∴', '∼' => '∼', '≅' => '≅', '≈' => '≈', '≠' => '≠', '≡' => '≡', '≤' => '≤', '≥' => '≥',
|
||||
'⊂' => '⊂', '⊃' => '⊃', '⊄' => '⊄', '⊆' => '⊆', '⊇' => '⊇', '⊕' => '⊕', '⊗' => '⊗', '⊥' => '⊥',
|
||||
'⋅' => '⋅', '⌈' => '⌈', '⌉' => '⌉', '⌊' => '⌊', '⌋' => '⌋', '⟨' => '〈', '⟩' => '〉', '◊' => '◊',
|
||||
'♠' => '♠', '♣' => '♣', '♥' => '♥', '♦' => '♦', '"' => '"', '&' => '&', '<' => '<', '>' => '>', 'Œ' => 'Œ',
|
||||
'œ' => 'œ', 'Š' => 'Š', 'š' => 'š', 'Ÿ' => 'Ÿ', 'ˆ' => 'ˆ', '˜' => '˜', ' ' => ' ', ' ' => ' ',
|
||||
' ' => ' ', '‌' => '‌', '‍' => '‍', '‎' => '‎', '‏' => '‏', '–' => '–', '—' => '—', '‘' => '‘',
|
||||
'’' => '’', '‚' => '‚', '“' => '“', '”' => '”', '„' => '„', '†' => '†', '‡' => '‡', '‰' => '‰',
|
||||
'‹' => '‹', '›' => '›', '€' => '€'
|
||||
];
|
||||
/**
|
||||
* HTML to XML conversion table for entities
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $entities = [
|
||||
' ' => ' ', '¡' => '¡', '¢' => '¢', '£' => '£', '¤' => '¤', '¥' => '¥', '¦' => '¦', '§' => '§',
|
||||
'¨' => '¨', '©' => '©', 'ª' => 'ª', '«' => '«', '¬' => '¬', '­' => '­', '®' => '®', '¯' => '¯',
|
||||
'°' => '°', '±' => '±', '²' => '²', '³' => '³', '´' => '´', 'µ' => 'µ', '¶' => '¶', '·' => '·',
|
||||
'¸' => '¸', '¹' => '¹', 'º' => 'º', '»' => '»', '¼' => '¼', '½' => '½', '¾' => '¾', '¿' => '¿',
|
||||
'À' => 'À', 'Á' => 'Á', 'Â' => 'Â', 'Ã' => 'Ã', 'Ä' => 'Ä', 'Å' => 'Å', 'Æ' => 'Æ', 'Ç' => 'Ç',
|
||||
'È' => 'È', 'É' => 'É', 'Ê' => 'Ê', 'Ë' => 'Ë', 'Ì' => 'Ì', 'Í' => 'Í', 'Î' => 'Î', 'Ï' => 'Ï',
|
||||
'Ð' => 'Ð', 'Ñ' => 'Ñ', 'Ò' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô', 'Õ' => 'Õ', 'Ö' => 'Ö', '×' => '×',
|
||||
'Ø' => 'Ø', 'Ù' => 'Ù', 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', 'Ý' => 'Ý', 'Þ' => 'Þ', 'ß' => 'ß',
|
||||
'à' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã', 'ä' => 'ä', 'å' => 'å', 'æ' => 'æ', 'ç' => 'ç',
|
||||
'è' => 'è', 'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'í' => 'í', 'î' => 'î', 'ï' => 'ï',
|
||||
'ð' => 'ð', 'ñ' => 'ñ', 'ò' => 'ò', 'ó' => 'ó', 'ô' => 'ô', 'õ' => 'õ', 'ö' => 'ö', '÷' => '÷',
|
||||
'ø' => 'ø', 'ù' => 'ù', 'ú' => 'ú', 'û' => 'û', 'ü' => 'ü', 'ý' => 'ý', 'þ' => 'þ', 'ÿ' => 'ÿ',
|
||||
'ƒ' => 'ƒ', 'Α' => 'Α', 'Β' => 'Β', 'Γ' => 'Γ', 'Δ' => 'Δ', 'Ε' => 'Ε', 'Ζ' => 'Ζ', 'Η' => 'Η',
|
||||
'Θ' => 'Θ', 'Ι' => 'Ι', 'Κ' => 'Κ', 'Λ' => 'Λ', 'Μ' => 'Μ', 'Ν' => 'Ν', 'Ξ' => 'Ξ', 'Ο' => 'Ο',
|
||||
'Π' => 'Π', 'Ρ' => 'Ρ', 'Σ' => 'Σ', 'Τ' => 'Τ', 'Υ' => 'Υ', 'Φ' => 'Φ', 'Χ' => 'Χ', 'Ψ' => 'Ψ',
|
||||
'Ω' => 'Ω', 'α' => 'α', 'β' => 'β', 'γ' => 'γ', 'δ' => 'δ', 'ε' => 'ε', 'ζ' => 'ζ', 'η' => 'η',
|
||||
'θ' => 'θ', 'ι' => 'ι', 'κ' => 'κ', 'λ' => 'λ', 'μ' => 'μ', 'ν' => 'ν', 'ξ' => 'ξ', 'ο' => 'ο',
|
||||
'π' => 'π', 'ρ' => 'ρ', 'ς' => 'ς', 'σ' => 'σ', 'τ' => 'τ', 'υ' => 'υ', 'φ' => 'φ', 'χ' => 'χ',
|
||||
'ψ' => 'ψ', 'ω' => 'ω', 'ϑ' => 'ϑ', 'ϒ' => 'ϒ', 'ϖ' => 'ϖ', '•' => '•', '…' => '…', '′' => '′',
|
||||
'″' => '″', '‾' => '‾', '⁄' => '⁄', '℘' => '℘', 'ℑ' => 'ℑ', 'ℜ' => 'ℜ', '™' => '™', 'ℵ' => 'ℵ',
|
||||
'←' => '←', '↑' => '↑', '→' => '→', '↓' => '↓', '↔' => '↔', '↵' => '↵', '⇐' => '⇐', '⇑' => '⇑',
|
||||
'⇒' => '⇒', '⇓' => '⇓', '⇔' => '⇔', '∀' => '∀', '∂' => '∂', '∃' => '∃', '∅' => '∅', '∇' => '∇',
|
||||
'∈' => '∈', '∉' => '∉', '∋' => '∋', '∏' => '∏', '∑' => '∑', '−' => '−', '∗' => '∗', '√' => '√',
|
||||
'∝' => '∝', '∞' => '∞', '∠' => '∠', '∧' => '∧', '∨' => '∨', '∩' => '∩', '∪' => '∪', '∫' => '∫',
|
||||
'∴' => '∴', '∼' => '∼', '≅' => '≅', '≈' => '≈', '≠' => '≠', '≡' => '≡', '≤' => '≤', '≥' => '≥',
|
||||
'⊂' => '⊂', '⊃' => '⊃', '⊄' => '⊄', '⊆' => '⊆', '⊇' => '⊇', '⊕' => '⊕', '⊗' => '⊗', '⊥' => '⊥',
|
||||
'⋅' => '⋅', '⌈' => '⌈', '⌉' => '⌉', '⌊' => '⌊', '⌋' => '⌋', '⟨' => '〈', '⟩' => '〉', '◊' => '◊',
|
||||
'♠' => '♠', '♣' => '♣', '♥' => '♥', '♦' => '♦', '"' => '"', '&' => '&', '<' => '<', '>' => '>', 'Œ' => 'Œ',
|
||||
'œ' => 'œ', 'Š' => 'Š', 'š' => 'š', 'Ÿ' => 'Ÿ', 'ˆ' => 'ˆ', '˜' => '˜', ' ' => ' ', ' ' => ' ',
|
||||
' ' => ' ', '‌' => '‌', '‍' => '‍', '‎' => '‎', '‏' => '‏', '–' => '–', '—' => '—', '‘' => '‘',
|
||||
'’' => '’', '‚' => '‚', '“' => '“', '”' => '”', '„' => '„', '†' => '†', '‡' => '‡', '‰' => '‰',
|
||||
'‹' => '‹', '›' => '›', '€' => '€'
|
||||
];
|
||||
|
||||
/**
|
||||
* Closing string for void tags
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $void = ' />';
|
||||
/**
|
||||
* Closing string for void tags
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static $void = ' />';
|
||||
|
||||
/**
|
||||
* Generates a single attribute or a list of attributes
|
||||
*
|
||||
* @param string|array $name String: A single attribute with that name will be generated.
|
||||
* Key-value array: A list of attributes will be generated. Don't pass a second argument in that case.
|
||||
* @param mixed $value If used with a `$name` string, pass the value of the attribute here.
|
||||
* If used with a `$name` array, this can be set to `false` to disable attribute sorting.
|
||||
* @return string|null The generated XML attributes string
|
||||
*/
|
||||
public static function attr($name, $value = null): ?string
|
||||
{
|
||||
if (is_array($name) === true) {
|
||||
if ($value !== false) {
|
||||
ksort($name);
|
||||
}
|
||||
/**
|
||||
* Generates a single attribute or a list of attributes
|
||||
*
|
||||
* @param string|array $name String: A single attribute with that name will be generated.
|
||||
* Key-value array: A list of attributes will be generated. Don't pass a second argument in that case.
|
||||
* @param mixed $value If used with a `$name` string, pass the value of the attribute here.
|
||||
* If used with a `$name` array, this can be set to `false` to disable attribute sorting.
|
||||
* @return string|null The generated XML attributes string
|
||||
*/
|
||||
public static function attr($name, $value = null): ?string
|
||||
{
|
||||
if (is_array($name) === true) {
|
||||
if ($value !== false) {
|
||||
ksort($name);
|
||||
}
|
||||
|
||||
$attributes = [];
|
||||
foreach ($name as $key => $val) {
|
||||
$a = static::attr($key, $val);
|
||||
$attributes = [];
|
||||
foreach ($name as $key => $val) {
|
||||
$a = static::attr($key, $val);
|
||||
|
||||
if ($a) {
|
||||
$attributes[] = $a;
|
||||
}
|
||||
}
|
||||
if ($a) {
|
||||
$attributes[] = $a;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' ', $attributes);
|
||||
}
|
||||
return implode(' ', $attributes);
|
||||
}
|
||||
|
||||
if ($value === null || $value === '' || $value === []) {
|
||||
return null;
|
||||
}
|
||||
if ($value === null || $value === '' || $value === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value === ' ') {
|
||||
return strtolower($name) . '=""';
|
||||
}
|
||||
if ($value === ' ') {
|
||||
return strtolower($name) . '=""';
|
||||
}
|
||||
|
||||
if (is_bool($value) === true) {
|
||||
return $value === true ? strtolower($name) . '="' . strtolower($name) . '"' : null;
|
||||
}
|
||||
if (is_bool($value) === true) {
|
||||
return $value === true ? strtolower($name) . '="' . strtolower($name) . '"' : null;
|
||||
}
|
||||
|
||||
if (is_array($value) === true) {
|
||||
if (isset($value['value'], $value['escape'])) {
|
||||
$value = $value['escape'] === true ? static::encode($value['value']) : $value['value'];
|
||||
} else {
|
||||
$value = implode(' ', array_filter(
|
||||
$value,
|
||||
fn ($value) => !empty($value) || is_numeric($value)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$value = static::encode($value);
|
||||
}
|
||||
if (is_array($value) === true) {
|
||||
if (isset($value['value'], $value['escape'])) {
|
||||
$value = $value['escape'] === true ? static::encode($value['value']) : $value['value'];
|
||||
} else {
|
||||
$value = implode(' ', array_filter(
|
||||
$value,
|
||||
fn ($value) => !empty($value) || is_numeric($value)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$value = static::encode($value);
|
||||
}
|
||||
|
||||
return strtolower($name) . '="' . $value . '"';
|
||||
}
|
||||
return strtolower($name) . '="' . $value . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an XML string from an array
|
||||
*
|
||||
* Supports special array keys `@name` (element name),
|
||||
* `@attributes` (XML attribute key-value array),
|
||||
* `@namespaces` (array with XML namespaces) and
|
||||
* `@value` (element content)
|
||||
*
|
||||
* @param array|string $props The source array or tag content (used internally)
|
||||
* @param string $name The name of the root element
|
||||
* @param bool $head Include the XML declaration head or not
|
||||
* @param string $indent Indentation string, defaults to two spaces
|
||||
* @param int $level The indentation level (used internally)
|
||||
* @return string The XML string
|
||||
*/
|
||||
public static function create($props, string $name = 'root', bool $head = true, string $indent = ' ', int $level = 0): string
|
||||
{
|
||||
if (is_array($props) === true) {
|
||||
if (A::isAssociative($props) === true) {
|
||||
// a tag with attributes or named children
|
||||
/**
|
||||
* Creates an XML string from an array
|
||||
*
|
||||
* Supports special array keys `@name` (element name),
|
||||
* `@attributes` (XML attribute key-value array),
|
||||
* `@namespaces` (array with XML namespaces) and
|
||||
* `@value` (element content)
|
||||
*
|
||||
* @param array|string $props The source array or tag content (used internally)
|
||||
* @param string $name The name of the root element
|
||||
* @param bool $head Include the XML declaration head or not
|
||||
* @param string $indent Indentation string, defaults to two spaces
|
||||
* @param int $level The indentation level (used internally)
|
||||
* @return string The XML string
|
||||
*/
|
||||
public static function create($props, string $name = 'root', bool $head = true, string $indent = ' ', int $level = 0): string
|
||||
{
|
||||
if (is_array($props) === true) {
|
||||
if (A::isAssociative($props) === true) {
|
||||
// a tag with attributes or named children
|
||||
|
||||
// extract metadata from special array keys
|
||||
$name = $props['@name'] ?? $name;
|
||||
$attributes = $props['@attributes'] ?? [];
|
||||
$value = $props['@value'] ?? null;
|
||||
if (isset($props['@namespaces'])) {
|
||||
foreach ($props['@namespaces'] as $key => $namespace) {
|
||||
$key = 'xmlns' . (($key) ? ':' . $key : '');
|
||||
$attributes[$key] = $namespace;
|
||||
}
|
||||
}
|
||||
// extract metadata from special array keys
|
||||
$name = $props['@name'] ?? $name;
|
||||
$attributes = $props['@attributes'] ?? [];
|
||||
$value = $props['@value'] ?? null;
|
||||
if (isset($props['@namespaces'])) {
|
||||
foreach ($props['@namespaces'] as $key => $namespace) {
|
||||
$key = 'xmlns' . (($key) ? ':' . $key : '');
|
||||
$attributes[$key] = $namespace;
|
||||
}
|
||||
}
|
||||
|
||||
// continue with just the children
|
||||
unset($props['@name'], $props['@attributes'], $props['@namespaces'], $props['@value']);
|
||||
// continue with just the children
|
||||
unset($props['@name'], $props['@attributes'], $props['@namespaces'], $props['@value']);
|
||||
|
||||
if (count($props) > 0) {
|
||||
// there are children, use them instead of the value
|
||||
if (count($props) > 0) {
|
||||
// there are children, use them instead of the value
|
||||
|
||||
$value = [];
|
||||
foreach ($props as $childName => $childItem) {
|
||||
// render the child, but don't include the indentation of the first line
|
||||
$value[] = trim(static::create($childItem, $childName, false, $indent, $level + 1));
|
||||
}
|
||||
}
|
||||
$value = [];
|
||||
foreach ($props as $childName => $childItem) {
|
||||
// render the child, but don't include the indentation of the first line
|
||||
$value[] = trim(static::create($childItem, $childName, false, $indent, $level + 1));
|
||||
}
|
||||
}
|
||||
|
||||
$result = static::tag($name, $value, $attributes, $indent, $level);
|
||||
} else {
|
||||
// just children
|
||||
$result = static::tag($name, $value, $attributes, $indent, $level);
|
||||
} else {
|
||||
// just children
|
||||
|
||||
$result = [];
|
||||
foreach ($props as $childItem) {
|
||||
$result[] = static::create($childItem, $name, false, $indent, $level);
|
||||
}
|
||||
$result = [];
|
||||
foreach ($props as $childItem) {
|
||||
$result[] = static::create($childItem, $name, false, $indent, $level);
|
||||
}
|
||||
|
||||
$result = implode(PHP_EOL, $result);
|
||||
}
|
||||
} else {
|
||||
// scalar value
|
||||
$result = implode(PHP_EOL, $result);
|
||||
}
|
||||
} else {
|
||||
// scalar value
|
||||
|
||||
$result = static::tag($name, $props, null, $indent, $level);
|
||||
}
|
||||
$result = static::tag($name, $props, null, $indent, $level);
|
||||
}
|
||||
|
||||
if ($head === true) {
|
||||
return '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL . $result;
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
if ($head === true) {
|
||||
return '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL . $result;
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all HTML/XML tags and encoded chars from a string
|
||||
*
|
||||
* ```
|
||||
* echo Xml::decode('some über <em>crazy</em> stuff');
|
||||
* // output: some über crazy stuff
|
||||
* ```
|
||||
*
|
||||
* @param string|null $string
|
||||
* @return string
|
||||
*/
|
||||
public static function decode(?string $string): string
|
||||
{
|
||||
if ($string === null) {
|
||||
$string = '';
|
||||
}
|
||||
/**
|
||||
* Removes all HTML/XML tags and encoded chars from a string
|
||||
*
|
||||
* ```
|
||||
* echo Xml::decode('some über <em>crazy</em> stuff');
|
||||
* // output: some über crazy stuff
|
||||
* ```
|
||||
*
|
||||
* @param string|null $string
|
||||
* @return string
|
||||
*/
|
||||
public static function decode(?string $string): string
|
||||
{
|
||||
if ($string === null) {
|
||||
$string = '';
|
||||
}
|
||||
|
||||
$string = strip_tags($string);
|
||||
return html_entity_decode($string, ENT_COMPAT, 'utf-8');
|
||||
}
|
||||
$string = strip_tags($string);
|
||||
return html_entity_decode($string, ENT_COMPAT, 'utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string to an XML-safe string
|
||||
*
|
||||
* Converts it to HTML-safe first and then it
|
||||
* will replace HTML entities with XML entities
|
||||
*
|
||||
* ```php
|
||||
* echo Xml::encode('some über crazy stuff');
|
||||
* // output: some über crazy stuff
|
||||
* ```
|
||||
*
|
||||
* @param string|null $string
|
||||
* @param bool $html True = Convert to HTML-safe first
|
||||
* @return string
|
||||
*/
|
||||
public static function encode(?string $string, bool $html = true): string
|
||||
{
|
||||
if ($string === null) {
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* Converts a string to an XML-safe string
|
||||
*
|
||||
* Converts it to HTML-safe first and then it
|
||||
* will replace HTML entities with XML entities
|
||||
*
|
||||
* ```php
|
||||
* echo Xml::encode('some über crazy stuff');
|
||||
* // output: some über crazy stuff
|
||||
* ```
|
||||
*
|
||||
* @param string|null $string
|
||||
* @param bool $html True = Convert to HTML-safe first
|
||||
* @return string
|
||||
*/
|
||||
public static function encode(?string $string, bool $html = true): string
|
||||
{
|
||||
if ($string === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($html === true) {
|
||||
$string = Html::encode($string, false);
|
||||
}
|
||||
if ($html === true) {
|
||||
$string = Html::encode($string, false);
|
||||
}
|
||||
|
||||
$entities = self::entities();
|
||||
$html = array_keys($entities);
|
||||
$xml = array_values($entities);
|
||||
$entities = self::entities();
|
||||
$html = array_keys($entities);
|
||||
$xml = array_values($entities);
|
||||
|
||||
return str_replace($html, $xml, $string);
|
||||
}
|
||||
return str_replace($html, $xml, $string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTML-to-XML entity translation table
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function entities(): array
|
||||
{
|
||||
return self::$entities;
|
||||
}
|
||||
/**
|
||||
* Returns the HTML-to-XML entity translation table
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function entities(): array
|
||||
{
|
||||
return self::$entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an XML string and returns an array
|
||||
*
|
||||
* @param string $xml
|
||||
* @return array|null Parsed array or `null` on error
|
||||
*/
|
||||
public static function parse(string $xml): ?array
|
||||
{
|
||||
$xml = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);
|
||||
/**
|
||||
* Parses an XML string and returns an array
|
||||
*
|
||||
* @param string $xml
|
||||
* @return array|null Parsed array or `null` on error
|
||||
*/
|
||||
public static function parse(string $xml): ?array
|
||||
{
|
||||
$xml = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);
|
||||
|
||||
if (is_object($xml) !== true) {
|
||||
return null;
|
||||
}
|
||||
if (is_object($xml) !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::simplify($xml);
|
||||
}
|
||||
return static::simplify($xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks a SimpleXMLElement down into a simpler tree
|
||||
* structure of arrays and strings
|
||||
*
|
||||
* @param \SimpleXMLElement $element
|
||||
* @param bool $collectName Whether the element name should be collected (for the root element)
|
||||
* @return array|string
|
||||
*/
|
||||
public static function simplify(SimpleXMLElement $element, bool $collectName = true)
|
||||
{
|
||||
// get all XML namespaces of the whole document to iterate over later;
|
||||
// we don't need the global namespace (empty string) in the list
|
||||
$usedNamespaces = $element->getNamespaces(true);
|
||||
if (isset($usedNamespaces[''])) {
|
||||
unset($usedNamespaces['']);
|
||||
}
|
||||
/**
|
||||
* Breaks a SimpleXMLElement down into a simpler tree
|
||||
* structure of arrays and strings
|
||||
*
|
||||
* @param \SimpleXMLElement $element
|
||||
* @param bool $collectName Whether the element name should be collected (for the root element)
|
||||
* @return array|string
|
||||
*/
|
||||
public static function simplify(SimpleXMLElement $element, bool $collectName = true)
|
||||
{
|
||||
// get all XML namespaces of the whole document to iterate over later;
|
||||
// we don't need the global namespace (empty string) in the list
|
||||
$usedNamespaces = $element->getNamespaces(true);
|
||||
if (isset($usedNamespaces[''])) {
|
||||
unset($usedNamespaces['']);
|
||||
}
|
||||
|
||||
// now collect element metadata of the parent
|
||||
$array = [];
|
||||
if ($collectName === true) {
|
||||
$array['@name'] = $element->getName();
|
||||
}
|
||||
// now collect element metadata of the parent
|
||||
$array = [];
|
||||
if ($collectName === true) {
|
||||
$array['@name'] = $element->getName();
|
||||
}
|
||||
|
||||
// collect attributes with each defined document namespace;
|
||||
// also check for attributes without any namespace
|
||||
$attributeArray = [];
|
||||
foreach (array_merge([0 => null], array_keys($usedNamespaces)) as $namespace) {
|
||||
$prefix = ($namespace) ? $namespace . ':' : '';
|
||||
$attributes = $element->attributes($namespace, true);
|
||||
// collect attributes with each defined document namespace;
|
||||
// also check for attributes without any namespace
|
||||
$attributeArray = [];
|
||||
foreach (array_merge([0 => null], array_keys($usedNamespaces)) as $namespace) {
|
||||
$prefix = ($namespace) ? $namespace . ':' : '';
|
||||
$attributes = $element->attributes($namespace, true);
|
||||
|
||||
foreach ($attributes as $key => $value) {
|
||||
$attributeArray[$prefix . $key] = (string)$value;
|
||||
}
|
||||
}
|
||||
if (count($attributeArray) > 0) {
|
||||
$array['@attributes'] = $attributeArray;
|
||||
}
|
||||
foreach ($attributes as $key => $value) {
|
||||
$attributeArray[$prefix . $key] = (string)$value;
|
||||
}
|
||||
}
|
||||
if (count($attributeArray) > 0) {
|
||||
$array['@attributes'] = $attributeArray;
|
||||
}
|
||||
|
||||
// collect namespace definitions of this particular XML element
|
||||
if ($namespaces = $element->getDocNamespaces(false, false)) {
|
||||
$array['@namespaces'] = $namespaces;
|
||||
}
|
||||
// collect namespace definitions of this particular XML element
|
||||
if ($namespaces = $element->getDocNamespaces(false, false)) {
|
||||
$array['@namespaces'] = $namespaces;
|
||||
}
|
||||
|
||||
// check for children with each defined document namespace;
|
||||
// also check for children without any namespace
|
||||
$hasChildren = false;
|
||||
foreach (array_merge([0 => null], array_keys($usedNamespaces)) as $namespace) {
|
||||
$prefix = ($namespace) ? $namespace . ':' : '';
|
||||
$children = $element->children($namespace, true);
|
||||
// check for children with each defined document namespace;
|
||||
// also check for children without any namespace
|
||||
$hasChildren = false;
|
||||
foreach (array_merge([0 => null], array_keys($usedNamespaces)) as $namespace) {
|
||||
$prefix = ($namespace) ? $namespace . ':' : '';
|
||||
$children = $element->children($namespace, true);
|
||||
|
||||
if (count($children) > 0) {
|
||||
// there are children, recursively simplify each one
|
||||
$hasChildren = true;
|
||||
if (count($children) > 0) {
|
||||
// there are children, recursively simplify each one
|
||||
$hasChildren = true;
|
||||
|
||||
// make a grouped collection of elements per element name
|
||||
foreach ($children as $child) {
|
||||
$array[$prefix . $child->getName()][] = static::simplify($child, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// make a grouped collection of elements per element name
|
||||
foreach ($children as $child) {
|
||||
$array[$prefix . $child->getName()][] = static::simplify($child, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasChildren === true) {
|
||||
// there were children of any namespace
|
||||
if ($hasChildren === true) {
|
||||
// there were children of any namespace
|
||||
|
||||
// reduce elements where there is only one item
|
||||
// of the respective type to a simple string;
|
||||
// don't do anything with special `@` metadata keys
|
||||
foreach ($array as $name => $item) {
|
||||
if (substr($name, 0, 1) !== '@' && count($item) === 1) {
|
||||
$array[$name] = $item[0];
|
||||
}
|
||||
}
|
||||
// reduce elements where there is only one item
|
||||
// of the respective type to a simple string;
|
||||
// don't do anything with special `@` metadata keys
|
||||
foreach ($array as $name => $item) {
|
||||
if (substr($name, 0, 1) !== '@' && count($item) === 1) {
|
||||
$array[$name] = $item[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
} else {
|
||||
// we didn't find any XML children above, only use the string value
|
||||
$element = (string)$element;
|
||||
return $array;
|
||||
} else {
|
||||
// we didn't find any XML children above, only use the string value
|
||||
$element = (string)$element;
|
||||
|
||||
if (count($array) > 0) {
|
||||
$array['@value'] = $element;
|
||||
if (count($array) > 0) {
|
||||
$array['@value'] = $element;
|
||||
|
||||
return $array;
|
||||
} else {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
} else {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an XML tag
|
||||
*
|
||||
* @param string $name Tag name
|
||||
* @param array|string|null $content Scalar value or array with multiple lines of content or `null` to
|
||||
* generate a self-closing tag; pass an empty string to generate empty content
|
||||
* @param array $attr An associative array with additional attributes for the tag
|
||||
* @param string|null $indent Indentation string, defaults to two spaces or `null` for output on one line
|
||||
* @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
|
||||
{
|
||||
$attr = static::attr($attr);
|
||||
$start = '<' . $name . ($attr ? ' ' . $attr : '') . '>';
|
||||
$startShort = '<' . $name . ($attr ? ' ' . $attr : '') . static::$void;
|
||||
$end = '</' . $name . '>';
|
||||
$baseIndent = $indent ? str_repeat($indent, $level) : '';
|
||||
/**
|
||||
* Builds an XML tag
|
||||
*
|
||||
* @param string $name Tag name
|
||||
* @param array|string|null $content Scalar value or array with multiple lines of content or `null` to
|
||||
* generate a self-closing tag; pass an empty string to generate empty content
|
||||
* @param array $attr An associative array with additional attributes for the tag
|
||||
* @param string|null $indent Indentation string, defaults to two spaces or `null` for output on one line
|
||||
* @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
|
||||
{
|
||||
$attr = static::attr($attr);
|
||||
$start = '<' . $name . ($attr ? ' ' . $attr : '') . '>';
|
||||
$startShort = '<' . $name . ($attr ? ' ' . $attr : '') . static::$void;
|
||||
$end = '</' . $name . '>';
|
||||
$baseIndent = $indent ? str_repeat($indent, $level) : '';
|
||||
|
||||
if (is_array($content) === true) {
|
||||
if (is_string($indent) === true) {
|
||||
$xml = $baseIndent . $start . PHP_EOL;
|
||||
foreach ($content as $line) {
|
||||
$xml .= $baseIndent . $indent . $line . PHP_EOL;
|
||||
}
|
||||
$xml .= $baseIndent . $end;
|
||||
} else {
|
||||
$xml = $start . implode($content) . $end;
|
||||
}
|
||||
} elseif ($content === null) {
|
||||
$xml = $baseIndent . $startShort;
|
||||
} else {
|
||||
$xml = $baseIndent . $start . static::value($content) . $end;
|
||||
}
|
||||
if (is_array($content) === true) {
|
||||
if (is_string($indent) === true) {
|
||||
$xml = $baseIndent . $start . PHP_EOL;
|
||||
foreach ($content as $line) {
|
||||
$xml .= $baseIndent . $indent . $line . PHP_EOL;
|
||||
}
|
||||
$xml .= $baseIndent . $end;
|
||||
} else {
|
||||
$xml = $start . implode($content) . $end;
|
||||
}
|
||||
} elseif ($content === null) {
|
||||
$xml = $baseIndent . $startShort;
|
||||
} else {
|
||||
$xml = $baseIndent . $start . static::value($content) . $end;
|
||||
}
|
||||
|
||||
return $xml;
|
||||
}
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly encodes tag contents
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return string|null
|
||||
*/
|
||||
public static function value($value): ?string
|
||||
{
|
||||
if ($value === true) {
|
||||
return 'true';
|
||||
}
|
||||
/**
|
||||
* Properly encodes tag contents
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return string|null
|
||||
*/
|
||||
public static function value($value): ?string
|
||||
{
|
||||
if ($value === true) {
|
||||
return 'true';
|
||||
}
|
||||
|
||||
if ($value === false) {
|
||||
return 'false';
|
||||
}
|
||||
if ($value === false) {
|
||||
return 'false';
|
||||
}
|
||||
|
||||
if (is_numeric($value) === true) {
|
||||
return (string)$value;
|
||||
}
|
||||
if (is_numeric($value) === true) {
|
||||
return (string)$value;
|
||||
}
|
||||
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Str::startsWith($value, '<![CDATA[') === true) {
|
||||
return $value;
|
||||
}
|
||||
if (Str::startsWith($value, '<![CDATA[') === true) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$encoded = htmlentities($value, ENT_NOQUOTES | ENT_XML1);
|
||||
if ($encoded === $value) {
|
||||
// no CDATA block needed
|
||||
return $value;
|
||||
}
|
||||
$encoded = htmlentities($value, ENT_NOQUOTES | ENT_XML1);
|
||||
if ($encoded === $value) {
|
||||
// no CDATA block needed
|
||||
return $value;
|
||||
}
|
||||
|
||||
// wrap everything in a CDATA block
|
||||
// and ensure that it is not closed in the input string
|
||||
return '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', $value) . ']]>';
|
||||
}
|
||||
// wrap everything in a CDATA block
|
||||
// and ensure that it is not closed in the input string
|
||||
return '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', $value) . ']]>';
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user