summaryrefslogtreecommitdiffstats
path: root/inc/util.inc.php
diff options
context:
space:
mode:
authorSimon Rettberg2022-05-02 18:49:09 +0200
committerSimon Rettberg2022-05-02 18:49:09 +0200
commit5eb8df7432a708284862e4b126e418265d36b4ab (patch)
treed8812cc2bd6245e0b02ca6866a4c14e977e1bb62 /inc/util.inc.php
parent[rebootcontrol] Show time of execution for WOL/reboot/shutdown (diff)
downloadslx-admin-5eb8df7432a708284862e4b126e418265d36b4ab.tar.gz
slx-admin-5eb8df7432a708284862e4b126e418265d36b4ab.tar.xz
slx-admin-5eb8df7432a708284862e4b126e418265d36b4ab.zip
[inc/Util] Add types, move error printing functions to their own class
Diffstat (limited to 'inc/util.inc.php')
-rw-r--r--inc/util.inc.php221
1 files changed, 38 insertions, 183 deletions
diff --git a/inc/util.inc.php b/inc/util.inc.php
index c3e70f89..d95265f4 100644
--- a/inc/util.inc.php
+++ b/inc/util.inc.php
@@ -5,158 +5,17 @@ class Util
private static $redirectParams = array();
/**
- * Displays an error message and stops script execution.
- * If CONFIG_DEBUG is true, it will also dump a stack trace
- * and all globally defined variables.
- * (As this might reveal sensitive data you should never enable it in production)
- */
- public static function traceError($message)
- {
- if ((defined('API') && API) || (defined('AJAX') && AJAX) || php_sapi_name() === 'cli') {
- error_log('API ERROR: ' . $message);
- error_log(self::formatBacktracePlain(debug_backtrace()));
- }
- if (php_sapi_name() === 'cli') {
- // Don't spam HTML when invoked via cli, above error_log should have gone to stdout/stderr
- exit(1);
- }
- Header('HTTP/1.1 500 Internal Server Error');
- if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'html') === false ) {
- Header('Content-Type: text/plain; charset=utf-8');
- echo 'API ERROR: ', $message, "\n", self::formatBacktracePlain(debug_backtrace());
- exit(0);
- }
- Header('Content-Type: text/html; charset=utf-8');
- echo '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><style>', "\n",
- ".arg { color: red; background: white; }\n",
- "h1 a { color: inherit; text-decoration: inherit; font-weight: inherit; }\n",
- '</style><title>Fatal Error</title></head><body>';
- echo '<h1>Flagrant <a href="https://www.youtube.com/watch?v=7rrZ-sA4FQc&t=2m2s" target="_blank">S</a>ystem error</h1>';
- echo "<h2>Message</h2><pre>$message</pre>";
- if (strpos($message, 'Database') !== false) {
- echo '<div><a href="install.php">Try running database setup</a></div>';
- }
- echo "<br><br>";
- if (defined('CONFIG_DEBUG') && CONFIG_DEBUG) {
- global $SLX_ERRORS;
- if (!empty($SLX_ERRORS)) {
- echo '<h2>PHP Errors</h2><pre>';
- foreach ($SLX_ERRORS as $error) {
- echo htmlspecialchars("{$error['errstr']} ({$error['errfile']}:{$error['errline']}\n");
- }
- echo '</pre>';
- }
- echo "<h2>Stack Trace</h2>";
- echo '<pre>', self::formatBacktraceHtml(debug_backtrace()), '</pre>';
- echo "<h2>Globals</h2><pre>";
- echo htmlspecialchars(print_r($GLOBALS, true));
- echo '</pre>';
- } else {
- echo <<<SADFACE
-<pre>
-________________________¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶________
-____________________¶¶¶___________________¶¶¶¶_____
-________________¶¶¶_________________________¶¶¶¶___
-______________¶¶______________________________¶¶¶__
-___________¶¶¶_________________________________¶¶¶_
-_________¶¶_____________________________________¶¶¶
-________¶¶_________¶¶¶¶¶___________¶¶¶¶¶_________¶¶
-______¶¶__________¶¶¶¶¶¶__________¶¶¶¶¶¶_________¶¶
-_____¶¶___________¶¶¶¶____________¶¶¶¶___________¶¶
-____¶¶___________________________________________¶¶
-___¶¶___________________________________________¶¶_
-__¶¶____________________¶¶¶¶____________________¶¶_
-_¶¶_______________¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶______________¶¶__
-_¶¶____________¶¶¶¶___________¶¶¶¶¶___________¶¶___
-¶¶¶_________¶¶¶__________________¶¶__________¶¶____
-¶¶_________¶______________________¶¶________¶¶_____
-¶¶¶______¶________________________¶¶_______¶¶______
-¶¶¶_____¶_________________________¶¶_____¶¶________
-_¶¶¶___________________________________¶¶__________
-__¶¶¶________________________________¶¶____________
-___¶¶¶____________________________¶¶_______________
-____¶¶¶¶______________________¶¶¶__________________
-_______¶¶¶¶¶_____________¶¶¶¶¶_____________________
-</pre>
-SADFACE;
- }
- echo '</body></html>';
- exit(0);
- }
-
- private static function formatArgument($arg, $expandArray = true)
- {
- if (is_string($arg)) {
- $arg = "'$arg'";
- } elseif (is_object($arg)) {
- $arg = 'instanceof ' . get_class($arg);
- } elseif (is_array($arg)) {
- if ($expandArray && count($arg) < 20) {
- $expanded = '';
- foreach ($arg as $key => $value) {
- if (!empty($expanded)) {
- $expanded .= ', ';
- }
- $expanded .= $key . ': ' . self::formatArgument($value, false);
- if (strlen($expanded) > 200)
- break;
- }
- if (strlen($expanded) <= 200)
- return '[' . $expanded . ']';
- }
- $arg = 'Array(' . count($arg) . ')';
- }
- return $arg;
- }
-
- public static function formatBacktraceHtml($trace)
- {
- $output = '';
- foreach ($trace as $idx => $line) {
- $args = array();
- foreach ($line['args'] as $arg) {
- $arg = self::formatArgument($arg);
- $args[] = '<span class="arg">' . htmlspecialchars($arg) . '</span>';
- }
- $frame = str_pad('#' . $idx, 3, ' ', STR_PAD_LEFT);
- $function = htmlspecialchars($line['function']);
- $args = implode(', ', $args);
- $file = preg_replace('~(/[^/]+)$~', '<b>$1</b>', htmlspecialchars($line['file']));
- // Add line
- $output .= $frame . ' ' . $function . '<b>(</b>'
- . $args . '<b>)</b>' . ' @ <i>' . $file . '</i>:' . $line['line'] . "\n";
- }
- return $output;
- }
-
- public static function formatBacktracePlain($trace)
- {
- $output = '';
- foreach ($trace as $idx => $line) {
- $args = array();
- foreach ($line['args'] as $arg) {
- $args[] = self::formatArgument($arg);
- }
- $frame = str_pad('#' . $idx, 3, ' ', STR_PAD_LEFT);
- $args = implode(', ', $args);
- // Add line
- $output .= "\n" . $frame . ' ' . $line['function'] . '('
- . $args . ')' . ' @ ' . $line['file'] . ':' . $line['line'];
- }
- return $output;
- }
-
- /**
* Redirects the user via a '302 Moved' header.
* An active session will be saved, any messages that haven't
* been displayed yet will be appended to the redirect.
+ *
* @param string|false $location Location to redirect to. "false" to redirect to same URL (useful after POSTs)
* @param bool $preferRedirectPost if true, use the value from $_POST['redirect'] instead of $location
*/
- public static function redirect($location = false, $preferRedirectPost = false)
+ public static function redirect($location = false, bool $preferRedirectPost = false)
{
if ($location === false) {
- $location = preg_replace('/([&?])message\[\]\=[^&]*/', '\1', $_SERVER['REQUEST_URI']);
+ $location = preg_replace('/([&?])message\[\]=[^&]*/', '\1', $_SERVER['REQUEST_URI']);
}
$messages = Message::toRequest();
if ($preferRedirectPost
@@ -189,7 +48,7 @@ SADFACE;
exit(0);
}
- public static function addRedirectParam($key, $value)
+ public static function addRedirectParam(string $key, string $value)
{
self::$redirectParams[] = $key . '=' . urlencode($value);
}
@@ -201,7 +60,7 @@ SADFACE;
* token, this function will return false and display an error.
* If the token matches, or the user is not logged in, it will return true.
*/
- public static function verifyToken()
+ public static function verifyToken(): bool
{
if (!User::isLoggedIn() && Session::get('token') === false)
return true;
@@ -218,12 +77,12 @@ SADFACE;
* _word_ is underlined
* \n is line break
*/
- public static function markup($string)
+ public static function markup(string $string): string
{
$string = htmlspecialchars($string);
- $string = preg_replace('#(^|[\n \-_/\.])\*(.+?)\*($|[ \-_/\.\!\?,:])#is', '$1<b>$2</b>$3', $string);
- $string = preg_replace('#(^|[\n \-\*/\.])_(.+?)_($|[ \-\*/\.\!\?,:])#is', '$1<u>$2</u>$3', $string);
- $string = preg_replace('#(^|[\n \-_\*\.])/(.+?)/($|[ \-_\*\.\!\?,:])#is', '$1<i>$2</i>$3', $string);
+ $string = preg_replace('#(^|[\n \-_/.])\*(.+?)\*($|[ \-_/.!?,:])#is', '$1<b>$2</b>$3', $string);
+ $string = preg_replace('#(^|[\n \-*/.])_(.+?)_($|[ \-*/.!?,:])#is', '$1<u>$2</u>$3', $string);
+ $string = preg_replace('#(^|[\n \-_*.])/(.+?)/($|[ \-_*.!?,:])#is', '$1<i>$2</i>$3', $string);
return nl2br($string);
}
@@ -236,7 +95,7 @@ SADFACE;
* @param int $shift how many units to skip, i.e. if you pass in KiB or MiB
* @return string human readable string representing the given file size
*/
- public static function readableFileSize($bytes, $decimals = -1, $shift = 0)
+ public static function readableFileSize(float $bytes, int $decimals = -1, int $shift = 0): string
{
// round doesn't reliably work for large floats, pick workaround depending on OS
if (PHP_INT_SIZE === 4) {
@@ -257,17 +116,26 @@ SADFACE;
return sprintf("%.{$decimals}f", $bytes) . "\xe2\x80\x89" . $sz[$factor + $shift];
}
- public static function sanitizeFilename($name)
+ public static function sanitizeFilename(string $name)
{
return preg_replace('/[^a-zA-Z0-9_\-]+/', '_', $name);
}
- public static function safePath($path, $prefix = '')
+ /**
+ * Make sure given path is not absolute, and does not contain '..', or weird characters.
+ * Returns sanitized path, or false if invalid. If prefix is given, also make sure
+ * $path starts with it.
+ *
+ * @param string $path path to check for safety
+ * @param string $prefix required prefix of $path
+ * @return false|string
+ */
+ public static function safePath(string $path, string $prefix = '')
{
if (empty($path))
return false;
$path = trim($path);
- if ($path[0] == '/' || preg_match('/[\x00-\x19\?\*]/', $path))
+ if ($path[0] == '/' || preg_match('/[\x00-\x19?*]/', $path))
return false;
if (strpos($path, '..') !== false)
return false;
@@ -288,7 +156,7 @@ SADFACE;
* @param int $code the code to turn into an error description
* @return string the error description
*/
- public static function uploadErrorString($code)
+ public static function uploadErrorString(int $code): string
{
switch ($code) {
case UPLOAD_ERR_INI_SIZE:
@@ -326,7 +194,7 @@ SADFACE;
* @param string $ip_addr input to check
* @return boolean true iff $ip_addr is a valid public ipv4 address
*/
- public static function isPublicIpv4($ip_addr)
+ public static function isPublicIpv4(string $ip_addr): bool
{
if (!preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $ip_addr))
return false;
@@ -348,23 +216,6 @@ SADFACE;
}
/**
- * Check whether $arrax contains all keys given in $keyList
- * @param array $array An array
- * @param array $keyList A list of strings which must all be valid keys in $array
- * @return boolean
- */
- public static function hasAllKeys($array, $keyList)
- {
- if (!is_array($array))
- return false;
- foreach ($keyList as $key) {
- if (!isset($array[$key]))
- return false;
- }
- return true;
- }
-
- /**
* Send a file to user for download.
*
* @param string $file path of local file
@@ -374,7 +225,7 @@ SADFACE;
* true: error while reading the file
* - on success, the function does not return
*/
- public static function sendFile($file, $name, $delete = false)
+ public static function sendFile(string $file, string $name, bool $delete = false): bool
{
while ((@ob_get_level()) > 0)
@ob_end_clean();
@@ -404,7 +255,7 @@ SADFACE;
* @param bool $secure true = only use strong random sources
* @return string|bool string of requested length, false on error
*/
- public static function randomBytes($length, $secure = true)
+ public static function randomBytes(int $length, bool $secure = true)
{
if (function_exists('random_bytes')) {
try {
@@ -453,7 +304,7 @@ SADFACE;
/**
* @return string a random UUID, v4.
*/
- public static function randomUuid()
+ public static function randomUuid(): string
{
$b = unpack('h8a/h4b/h12c', self::randomBytes(12));
return sprintf('%08s-%04s-%04x-%04x-%012s',
@@ -481,10 +332,11 @@ SADFACE;
/**
* Transform timestamp to easily readable string.
* The format depends on how far the timestamp lies in the past.
+ *
* @param int $ts unix timestamp
* @return string human readable representation
*/
- public static function prettyTime($ts)
+ public static function prettyTime(int $ts): string
{
settype($ts, 'int');
if ($ts === 0)
@@ -507,10 +359,11 @@ SADFACE;
/**
* Return localized strings for yes or no depending on $bool
+ *
* @param bool $bool Input to evaluate
* @return string Yes or No, in user's selected language
*/
- public static function boolToString($bool)
+ public static function boolToString(bool $bool): string
{
if ($bool)
return Dictionary::translate('lang_yes', true);
@@ -519,11 +372,12 @@ SADFACE;
/**
* Format a duration, in seconds, into a readable string.
+ *
* @param int $seconds The number to format
- * @param int $showSecs whether to show seconds, or rather cut after minutes
+ * @param bool $showSecs whether to show seconds, or rather cut after minutes
* @return string
*/
- public static function formatDuration($seconds, $showSecs = true)
+ public static function formatDuration(int $seconds, bool $showSecs = true): string
{
settype($seconds, 'int');
static $UNITS = ['y' => 31536000, 'mon' => 2592000, 'd' => 86400];
@@ -550,9 +404,10 @@ SADFACE;
* was a weird problem where firefox would keep sending a cookie with
* path /slx-admin/ but trying to delete it from /slx-admin, which php's
* setcookie automatically sends by default, did not clear it.
+ *
* @param string $name cookie name
*/
- public static function clearCookie($name)
+ public static function clearCookie(string $name)
{
$parts = explode('/', $_SERVER['SCRIPT_NAME']);
$path = '';
@@ -567,7 +422,7 @@ SADFACE;
/**
* Remove any non-utf8 sequences from string.
*/
- public static function cleanUtf8(string $string) : string
+ public static function cleanUtf8(string $string): string
{
// https://stackoverflow.com/a/1401716/2043481
$regex = '/
@@ -586,7 +441,7 @@ SADFACE;
/**
* Remove non-printable < 0x20 chars from ANSI string, then convert to UTF-8
*/
- public static function ansiToUtf8(string $string) : string
+ public static function ansiToUtf8(string $string): string
{
$regex = '/
(