Upgrade to 3.9.2
This commit is contained in:
@@ -4,6 +4,7 @@ namespace Kirby\Toolkit;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* The `A` class provides a set of handy methods
|
||||
@@ -161,6 +162,33 @@ class A
|
||||
return implode($separator, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array and makes it associative by an argument.
|
||||
* If the argument is a callable, it will be used to map the array.
|
||||
* If it is a string, it will be used as a key to pluck from the array.
|
||||
*
|
||||
* <code>
|
||||
* $array = [['id'=>1], ['id'=>2], ['id'=>3]];
|
||||
* $keyed = A::keyBy($array, 'id');
|
||||
*
|
||||
* // Now you can access the array by the id
|
||||
* </code>
|
||||
*
|
||||
* @param array $array
|
||||
* @param string|callable $keyBy
|
||||
* @return array
|
||||
*/
|
||||
public static function keyBy(array $array, string|callable $keyBy): array
|
||||
{
|
||||
$keys = is_callable($keyBy) ? static::map($array, $keyBy) : static::pluck($array, $keyBy);
|
||||
|
||||
if (count($keys) !== count($array)) {
|
||||
throw new InvalidArgumentException('The "key by" argument must be a valid key or a callable');
|
||||
}
|
||||
|
||||
return array_combine($keys, $array);
|
||||
}
|
||||
|
||||
public const MERGE_OVERWRITE = 0;
|
||||
public const MERGE_APPEND = 1;
|
||||
public const MERGE_REPLACE = 2;
|
||||
@@ -404,15 +432,14 @@ class A
|
||||
* @param int $limit The number of elements the array should
|
||||
* contain after filling it up.
|
||||
* @param mixed $fill The element, which should be used to
|
||||
* fill the array
|
||||
* fill the array. If it's a callable, it
|
||||
* will be called with the current index
|
||||
* @return array The filled-up result array
|
||||
*/
|
||||
public static function fill(array $array, int $limit, $fill = 'placeholder'): array
|
||||
{
|
||||
$diff = $limit - count($array);
|
||||
|
||||
for ($x = 0; $x < $diff; $x++) {
|
||||
$array[] = $fill;
|
||||
for ($x = count($array); $x < $limit; $x++) {
|
||||
$array[] = is_callable($fill) ? $fill($x) : $fill;
|
||||
}
|
||||
|
||||
return $array;
|
||||
|
@@ -8,11 +8,11 @@ use DOMDocument;
|
||||
use DOMDocumentType;
|
||||
use DOMElement;
|
||||
use DOMNode;
|
||||
use DOMNodeList;
|
||||
use DOMProcessingInstruction;
|
||||
use DOMText;
|
||||
use DOMXPath;
|
||||
use Kirby\Cms\App;
|
||||
use Kirby\Exception\Exception;
|
||||
use Kirby\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
@@ -122,20 +122,16 @@ class Dom
|
||||
|
||||
/**
|
||||
* Returns the HTML body if one exists
|
||||
*
|
||||
* @return \DOMElement|null
|
||||
*/
|
||||
public function body()
|
||||
public function body(): DOMElement|null
|
||||
{
|
||||
return $this->body ??= $this->query('/html/body')[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the document object
|
||||
*
|
||||
* @return \DOMDocument
|
||||
*/
|
||||
public function document()
|
||||
public function document(): DOMDocument
|
||||
{
|
||||
return $this->doc;
|
||||
}
|
||||
@@ -143,9 +139,6 @@ class Dom
|
||||
/**
|
||||
* Extracts all URLs wrapped in a url() wrapper. E.g. for style attributes.
|
||||
* @internal
|
||||
*
|
||||
* @param string $value
|
||||
* @return array
|
||||
*/
|
||||
public static function extractUrls(string $value): array
|
||||
{
|
||||
@@ -170,12 +163,12 @@ class Dom
|
||||
* Checks for allowed attributes according to the allowlist
|
||||
* @internal
|
||||
*
|
||||
* @param \DOMAttr $attr
|
||||
* @param array $options
|
||||
* @return true|string If not allowed, an error message is returned
|
||||
*/
|
||||
public static function isAllowedAttr(DOMAttr $attr, array $options)
|
||||
{
|
||||
public static function isAllowedAttr(
|
||||
DOMAttr $attr,
|
||||
array $options
|
||||
): bool|string {
|
||||
$allowedTags = $options['allowedTags'];
|
||||
|
||||
// check if the attribute is in the list of global allowed attributes
|
||||
@@ -218,12 +211,12 @@ class Dom
|
||||
* Checks for allowed attributes according to the global allowlist
|
||||
* @internal
|
||||
*
|
||||
* @param \DOMAttr $attr
|
||||
* @param array $options
|
||||
* @return true|string If not allowed, an error message is returned
|
||||
*/
|
||||
public static function isAllowedGlobalAttr(DOMAttr $attr, array $options)
|
||||
{
|
||||
public static function isAllowedGlobalAttr(
|
||||
DOMAttr $attr,
|
||||
array $options
|
||||
): bool|string {
|
||||
$allowedAttrs = $options['allowedAttrs'];
|
||||
|
||||
if ($allowedAttrs === true) {
|
||||
@@ -256,12 +249,12 @@ class Dom
|
||||
* Checks if the URL is acceptable for URL attributes
|
||||
* @internal
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $options
|
||||
* @return true|string If not allowed, an error message is returned
|
||||
*/
|
||||
public static function isAllowedUrl(string $url, array $options)
|
||||
{
|
||||
public static function isAllowedUrl(
|
||||
string $url,
|
||||
array $options
|
||||
): bool|string {
|
||||
$url = Str::lower($url);
|
||||
|
||||
// allow empty URL values
|
||||
@@ -393,8 +386,6 @@ class Dom
|
||||
* Otherwise DOMDocument won't be available and the Dom cannot
|
||||
* work at all.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function isSupported(): bool
|
||||
@@ -404,9 +395,6 @@ class Dom
|
||||
|
||||
/**
|
||||
* Returns the XML or HTML markup contained in the node
|
||||
*
|
||||
* @param \DOMNode $node
|
||||
* @return string
|
||||
*/
|
||||
public function innerMarkup(DOMNode $node): string
|
||||
{
|
||||
@@ -425,14 +413,16 @@ class Dom
|
||||
* the allowed namespaces
|
||||
* @internal
|
||||
*
|
||||
* @param array $list
|
||||
* @param \DOMNode $node
|
||||
* @param array $options See `Dom::sanitize()`
|
||||
* @param \Closure|null Comparison callback that returns whether the expected and real name match
|
||||
* @return string|false Matched name in the list or `false`
|
||||
*/
|
||||
public static function listContainsName(array $list, DOMNode $node, array $options, Closure|null $compare = null)
|
||||
{
|
||||
public static function listContainsName(
|
||||
array $list,
|
||||
DOMNode $node,
|
||||
array $options,
|
||||
Closure|null $compare = null
|
||||
): string|false {
|
||||
$allowedNamespaces = $options['allowedNamespaces'];
|
||||
$localName = $node->localName;
|
||||
$compare ??= fn ($expected, $real): bool => $expected === $real;
|
||||
@@ -474,13 +464,19 @@ class Dom
|
||||
}
|
||||
|
||||
// try if we can find an exact namespaced match
|
||||
if ($namespaceUri === $node->namespaceURI && $compare($itemLocal, $localName) === true) {
|
||||
if (
|
||||
$namespaceUri === $node->namespaceURI &&
|
||||
$compare($itemLocal, $localName) === true
|
||||
) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
// also try to match the fully-qualified name
|
||||
// if the document doesn't define the namespace
|
||||
if ($node->namespaceURI === null && $compare($item, $node->nodeName) === true) {
|
||||
if (
|
||||
$node->namespaceURI === null &&
|
||||
$compare($item, $node->nodeName) === true
|
||||
) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
@@ -490,9 +486,6 @@ class Dom
|
||||
|
||||
/**
|
||||
* Removes a node from the document
|
||||
*
|
||||
* @param \DOMNode $node
|
||||
* @return void
|
||||
*/
|
||||
public static function remove(DOMNode $node): void
|
||||
{
|
||||
@@ -502,12 +495,12 @@ class Dom
|
||||
/**
|
||||
* Executes an XPath query in the document
|
||||
*
|
||||
* @param string $query
|
||||
* @param \DOMNode|null $node Optional context node for relative queries
|
||||
* @return \DOMNodeList|false
|
||||
*/
|
||||
public function query(string $query, ?DOMNode $node = null)
|
||||
{
|
||||
public function query(
|
||||
string $query,
|
||||
DOMNode|null $node = null
|
||||
): DOMNodeList|false {
|
||||
return (new DOMXPath($this->doc))->query($query, $node);
|
||||
}
|
||||
|
||||
@@ -602,7 +595,6 @@ class Dom
|
||||
* is exported with an XML declaration/
|
||||
* full HTML markup even if the input
|
||||
* didn't have them
|
||||
* @return string
|
||||
*/
|
||||
public function toString(bool $normalize = false): string
|
||||
{
|
||||
@@ -623,9 +615,6 @@ class Dom
|
||||
/**
|
||||
* Removes a node from the document but keeps its children
|
||||
* by moving them one level up
|
||||
*
|
||||
* @param \DOMNode $node
|
||||
* @return void
|
||||
*/
|
||||
public static function unwrap(DOMNode $node): void
|
||||
{
|
||||
@@ -648,7 +637,6 @@ class Dom
|
||||
* @param bool $normalize If set to `true`, the document
|
||||
* is exported with full HTML markup
|
||||
* even if the input didn't have it
|
||||
* @return string
|
||||
*/
|
||||
protected function exportHtml(bool $normalize = false): string
|
||||
{
|
||||
@@ -688,11 +676,13 @@ class Dom
|
||||
* @param bool $normalize If set to `true`, the document
|
||||
* is exported with an XML declaration
|
||||
* even if the input didn't have it
|
||||
* @return string
|
||||
*/
|
||||
protected function exportXml(bool $normalize = false): string
|
||||
{
|
||||
if (Str::contains($this->code, '<?xml ', true) === false && $normalize === false) {
|
||||
if (
|
||||
Str::contains($this->code, '<?xml ', true) === false &&
|
||||
$normalize === false
|
||||
) {
|
||||
// the input didn't contain an XML declaration;
|
||||
// only return child nodes, which omits it
|
||||
$result = [];
|
||||
@@ -714,13 +704,14 @@ class Dom
|
||||
/**
|
||||
* Sanitizes an attribute
|
||||
*
|
||||
* @param \DOMAttr $attr
|
||||
* @param array $options See `Dom::sanitize()`
|
||||
* @param array $errors Array to store additional errors in by reference
|
||||
* @return void
|
||||
*/
|
||||
protected function sanitizeAttr(DOMAttr $attr, array $options, array &$errors): void
|
||||
{
|
||||
protected function sanitizeAttr(
|
||||
DOMAttr $attr,
|
||||
array $options,
|
||||
array &$errors
|
||||
): void {
|
||||
$element = $attr->ownerElement;
|
||||
$name = $attr->nodeName;
|
||||
$value = $attr->value;
|
||||
@@ -762,13 +753,14 @@ class Dom
|
||||
/**
|
||||
* Sanitizes the doctype
|
||||
*
|
||||
* @param \DOMDocumentType $doctype
|
||||
* @param array $options See `Dom::sanitize()`
|
||||
* @param array $errors Array to store additional errors in by reference
|
||||
* @return void
|
||||
*/
|
||||
protected function sanitizeDoctype(DOMDocumentType $doctype, array $options, array &$errors): void
|
||||
{
|
||||
protected function sanitizeDoctype(
|
||||
DOMDocumentType $doctype,
|
||||
array $options,
|
||||
array &$errors
|
||||
): void {
|
||||
try {
|
||||
$this->validateDoctype($doctype, $options);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
@@ -780,13 +772,14 @@ class Dom
|
||||
/**
|
||||
* Sanitizes a single DOM element and its attribute
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
* @param array $options See `Dom::sanitize()`
|
||||
* @param array $errors Array to store additional errors in by reference
|
||||
* @return void
|
||||
*/
|
||||
protected function sanitizeElement(DOMElement $element, array $options, array &$errors): void
|
||||
{
|
||||
protected function sanitizeElement(
|
||||
DOMElement $element,
|
||||
array $options,
|
||||
array &$errors
|
||||
): void {
|
||||
$name = $element->nodeName;
|
||||
|
||||
// check defined namespaces (`xmlns` attributes);
|
||||
@@ -862,13 +855,14 @@ class Dom
|
||||
/**
|
||||
* Sanitizes a single XML processing instruction
|
||||
*
|
||||
* @param \DOMProcessingInstruction $pi
|
||||
* @param array $options See `Dom::sanitize()`
|
||||
* @param array $errors Array to store additional errors in by reference
|
||||
* @return void
|
||||
*/
|
||||
protected function sanitizePI(DOMProcessingInstruction $pi, array $options, array &$errors): void
|
||||
{
|
||||
protected function sanitizePI(
|
||||
DOMProcessingInstruction $pi,
|
||||
array $options,
|
||||
array &$errors
|
||||
): void {
|
||||
$name = $pi->nodeName;
|
||||
|
||||
// check for allow-listed processing instructions
|
||||
@@ -884,15 +878,18 @@ class Dom
|
||||
/**
|
||||
* Validates the document type
|
||||
*
|
||||
* @param \DOMDocumentType $doctype
|
||||
* @param array $options See `Dom::sanitize()`
|
||||
* @return void
|
||||
*
|
||||
* @throws \Kirby\Exception\InvalidArgumentException If the doctype is not valid
|
||||
*/
|
||||
protected function validateDoctype(DOMDocumentType $doctype, array $options): void
|
||||
{
|
||||
if (empty($doctype->publicId) === false || empty($doctype->systemId) === false) {
|
||||
protected function validateDoctype(
|
||||
DOMDocumentType $doctype,
|
||||
array $options
|
||||
): void {
|
||||
if (
|
||||
empty($doctype->publicId) === false ||
|
||||
empty($doctype->systemId) === false
|
||||
) {
|
||||
throw new InvalidArgumentException('The doctype must not reference external files');
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user