2022-08-31 15:02:43 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Kirby\Cms;
|
|
|
|
|
|
|
|
use Closure;
|
|
|
|
use Kirby\Exception\InvalidArgumentException;
|
|
|
|
use Kirby\Toolkit\Str;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The `Helpers` class hosts a few handy helper methods
|
|
|
|
* @since 3.7.0
|
|
|
|
*
|
|
|
|
* @package Kirby Cms
|
|
|
|
* @author Nico Hoffmann <nico@getkirby.com>
|
|
|
|
* @link https://getkirby.com
|
|
|
|
* @copyright Bastian Allgeier
|
|
|
|
* @license https://getkirby.com/license
|
|
|
|
*/
|
|
|
|
class Helpers
|
|
|
|
{
|
2023-04-14 16:34:06 +02:00
|
|
|
/**
|
|
|
|
* Allows to disable specific deprecation warnings
|
|
|
|
* by setting them to `false`.
|
|
|
|
* You can do this by putting the following code in
|
|
|
|
* `site/config/config.php`:
|
|
|
|
*
|
|
|
|
* ```php
|
|
|
|
* Helpers::$deprecations['<deprecation-key>'] = false;
|
|
|
|
* ```
|
|
|
|
*/
|
|
|
|
public static $deprecations = [
|
2025-04-21 18:57:21 +02:00
|
|
|
// The internal `$model->contentFile*()` methods have been deprecated
|
|
|
|
'model-content-file' => true,
|
|
|
|
|
|
|
|
// Passing an `info` array inside the `extends` array
|
|
|
|
// has been deprecated. Pass the individual entries (e.g. root, version)
|
|
|
|
// directly as named arguments.
|
|
|
|
// TODO: switch to true in v6
|
|
|
|
'plugin-extends-root' => false,
|
|
|
|
|
|
|
|
// Passing a single space as value to `Xml::attr()` has been
|
|
|
|
// deprecated. In a future version, passing a single space won't
|
|
|
|
// render an empty value anymore but a single space.
|
|
|
|
// To render an empty value, please pass an empty string.
|
|
|
|
'xml-attr-single-space' => true,
|
2023-04-14 16:34:06 +02:00
|
|
|
];
|
|
|
|
|
2022-08-31 15:02:43 +02:00
|
|
|
/**
|
|
|
|
* Triggers a deprecation warning if debug mode is active
|
2023-04-14 16:34:06 +02:00
|
|
|
* and warning has not been surpressed via `Helpers::$deprecations`
|
2022-08-31 15:02:43 +02:00
|
|
|
*
|
2023-04-14 16:34:06 +02:00
|
|
|
* @param string|null $key If given, the key will be checked against the static array
|
2022-08-31 15:02:43 +02:00
|
|
|
* @return bool Whether the warning was triggered
|
|
|
|
*/
|
2025-04-21 18:57:21 +02:00
|
|
|
public static function deprecated(
|
|
|
|
string $message,
|
|
|
|
string|null $key = null
|
|
|
|
): bool {
|
2023-04-14 16:34:06 +02:00
|
|
|
// only trigger warning in debug mode or when running PHPUnit tests
|
|
|
|
// @codeCoverageIgnoreStart
|
|
|
|
if (
|
|
|
|
App::instance()->option('debug') !== true &&
|
|
|
|
(defined('KIRBY_TESTING') !== true || KIRBY_TESTING !== true)
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// @codeCoverageIgnoreEnd
|
|
|
|
|
|
|
|
// don't trigger the warning if disabled by default or by the dev
|
|
|
|
if ($key !== null && (static::$deprecations[$key] ?? true) === false) {
|
|
|
|
return false;
|
2022-08-31 15:02:43 +02:00
|
|
|
}
|
|
|
|
|
2023-04-14 16:34:06 +02:00
|
|
|
return trigger_error($message, E_USER_DEPRECATED) === true;
|
2022-08-31 15:02:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simple object and variable dumper
|
|
|
|
* to help with debugging.
|
|
|
|
*/
|
2025-04-21 18:57:21 +02:00
|
|
|
public static function dump(mixed $variable, bool $echo = true): string
|
2022-08-31 15:02:43 +02:00
|
|
|
{
|
2025-04-21 18:57:21 +02:00
|
|
|
$kirby = App::instance();
|
|
|
|
$output = print_r($variable, true);
|
2022-12-19 14:56:05 +01:00
|
|
|
|
|
|
|
if ($kirby->environment()->cli() === true) {
|
2025-04-21 18:57:21 +02:00
|
|
|
$output .= PHP_EOL;
|
2022-12-19 14:56:05 +01:00
|
|
|
} else {
|
2025-04-21 18:57:21 +02:00
|
|
|
$output = Str::wrap($output, '<pre>', '</pre>');
|
2022-12-19 14:56:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($echo === true) {
|
|
|
|
echo $output;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $output;
|
2022-08-31 15:02:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Performs an action with custom handling
|
|
|
|
* for all PHP errors and warnings
|
|
|
|
* @since 3.7.4
|
|
|
|
*
|
|
|
|
* @param \Closure $action Any action that may cause an error or warning
|
2022-12-19 14:56:05 +01:00
|
|
|
* @param \Closure $condition Closure that returns bool to determine if to
|
|
|
|
* suppress an error, receives arguments for
|
|
|
|
* `set_error_handler()`
|
|
|
|
* @param mixed $fallback Value to return when error is suppressed
|
|
|
|
* @return mixed Return value of the `$action` closure,
|
|
|
|
* possibly overridden by `$fallback`
|
2022-08-31 15:02:43 +02:00
|
|
|
*/
|
2025-04-21 18:57:21 +02:00
|
|
|
public static function handleErrors(
|
|
|
|
Closure $action,
|
|
|
|
Closure $condition,
|
|
|
|
$fallback = null
|
|
|
|
) {
|
2022-12-19 14:56:05 +01:00
|
|
|
$override = null;
|
2022-08-31 15:02:43 +02:00
|
|
|
|
2025-04-21 18:57:21 +02:00
|
|
|
/**
|
|
|
|
* @psalm-suppress UndefinedVariable
|
|
|
|
*/
|
2022-12-19 14:56:05 +01:00
|
|
|
$handler = set_error_handler(function () use (&$override, &$handler, $condition, $fallback) {
|
|
|
|
// check if suppress condition is met
|
|
|
|
$suppress = $condition(...func_get_args());
|
|
|
|
|
|
|
|
if ($suppress !== true) {
|
2022-08-31 15:02:43 +02:00
|
|
|
// handle other warnings with Whoops if loaded
|
2022-12-19 14:56:05 +01:00
|
|
|
if (is_callable($handler) === true) {
|
|
|
|
return $handler(...func_get_args());
|
2022-08-31 15:02:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise use the standard error handler
|
|
|
|
return false; // @codeCoverageIgnore
|
|
|
|
}
|
2022-12-19 14:56:05 +01:00
|
|
|
|
|
|
|
// use fallback to override return for suppressed errors
|
|
|
|
$override = $fallback;
|
|
|
|
|
|
|
|
if (is_callable($override) === true) {
|
|
|
|
$override = $override();
|
|
|
|
}
|
2022-08-31 15:02:43 +02:00
|
|
|
|
|
|
|
// no additional error handling
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2025-04-21 18:57:21 +02:00
|
|
|
try {
|
|
|
|
$result = $action();
|
|
|
|
} finally {
|
|
|
|
// always restore the error handler, even if the
|
|
|
|
// action or the standard error handler threw an
|
|
|
|
// exception; this avoids modifying global state
|
|
|
|
restore_error_handler();
|
|
|
|
}
|
2022-08-31 15:02:43 +02:00
|
|
|
|
|
|
|
return $override ?? $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if a helper was overridden by the user
|
|
|
|
* by setting the `KIRBY_HELPER_*` constant
|
|
|
|
* @internal
|
|
|
|
*
|
|
|
|
* @param string $name Name of the helper
|
|
|
|
*/
|
|
|
|
public static function hasOverride(string $name): bool
|
|
|
|
{
|
|
|
|
$name = 'KIRBY_HELPER_' . strtoupper($name);
|
|
|
|
return defined($name) === true && constant($name) === false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines the size/length of numbers,
|
|
|
|
* strings, arrays and countable objects
|
|
|
|
*
|
|
|
|
* @throws \Kirby\Exception\InvalidArgumentException
|
|
|
|
*/
|
2025-04-21 18:57:21 +02:00
|
|
|
public static function size(mixed $value): int
|
2022-08-31 15:02:43 +02:00
|
|
|
{
|
|
|
|
if (is_numeric($value)) {
|
|
|
|
return (int)$value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_string($value)) {
|
|
|
|
return Str::length(trim($value));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_countable($value)) {
|
|
|
|
return count($value);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new InvalidArgumentException('Could not determine the size of the given value');
|
|
|
|
}
|
|
|
|
}
|