/** * Close all buffers and return content * * @param bool $getContent If true it returns buffer content, otherwise it is discarded * * @return string */ public static function obCleanAll($getContent = true) { $result = ''; for ($i = 0; $i < ob_get_level(); $i++) { if ($getContent) { $result .= ob_get_contents(); } ob_clean(); } return $result; } /** * Array map recursively * * @param callable $callback callback function * @param mixed[] $array array input * * @return mixed[] */ public static function arrayMapRecursive($callback, $array) { if (!is_array($array)) { throw new Exception('$array must be an array'); } if (!is_callable($callback)) { throw new Exception('$callback must be callable'); } $func = function ($item) use (&$func, &$callback) { return is_array($item) ? array_map($func, $item) : call_user_func($callback, $item); }; return array_map($func, $array); } /** * Implemented array_key_first * * @link https://www.php.net/manual/en/function.array-key-first.php * * @param mixed[] $arr array input * * @return int|string|null */ public static function arrayKeyFirst($arr) { if (!function_exists('array_key_first')) { foreach ($arr as $key => $unused) { return $key; } return null; } else { // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.array_key_firstFound return array_key_first($arr); } } /** * Get number of bit supported by PHP * * @return string */ public static function getArchitectureString() { return (PHP_INT_SIZE * 8) . '-bit'; } /** * In array check by callback * * @param mixed[] $haystack array input * @param callable $callback callback function * * @return null|bool */ public static function inArrayExtended($haystack, $callback) { if (!is_callable($callback)) { return null; } foreach ($haystack as $value) { if (call_user_func($callback, $value)) { return true; } } return false; } /** * This is a binary recursion, so it only works on an ordered array of integers. * (If it is not ordered, it does not work) * * The advantage of using this search instead of normal array search is that the complexity goes from O(n) to O(log n). * * @param int[] $array array values * @param int $x element to search * * @return bool */ public static function binarySearch($array, $x) { if (count($array) === 0) { return false; } $low = 0; $high = count($array) - 1; while ($low <= $high) { $mid = floor(($low + $high) / 2); if ($array[$mid] == $x) { return true; } if ($x < $array[$mid]) { $high = $mid - 1; } else { $low = $mid + 1; } } return false; } /** * Generates a random password drawn from the defined set of characters. * Copy of the wp_generate_password() function from wp-includes/pluggable.php with minor tweaks * * @param int $length Optional. The length of password to generate. Default 12. * @param bool $special_chars Optional. Whether to include standard special characters. * Default true. * @param bool $extra_special_chars Optional. Whether to include other special characters. * Used when generating secret keys and salts. Default false. * * @return string The random password. */ public static function generatePassword($length = 12, $special_chars = true, $extra_special_chars = false) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; if ($special_chars) { $chars .= '!@#$%^&*()'; } if ($extra_special_chars) { $chars .= '-_ []{}<>~`+=,.;:/?|'; } $password = ''; for ($i = 0; $i < $length; $i++) { $password .= substr($chars, self::rand(0, strlen($chars) - 1), 1); } return $password; } /** * Generates a random number * Copy of the wp_rand() function from wp-includes/pluggable.php with minor tweaks * * @param int $min Lower limit for the generated number * @param int $max Upper limit for the generated number * * @return int A random number between min and max */ public static function rand($min = 0, $max = 0) { global $rnd_value; // Some misconfigured 32bit environments (Entropy PHP, for example) truncate integers // larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats. $max_random_number = 3000000000 === 2147483647 ? (float) "4294967295" : 4294967295; // @phpstan-ignore-line // 4294967295 = 0xffffffff // We only handle Ints, floats are truncated to their integer value. $min = (int) $min; $max = (int) $max; // Use PHP's CSPRNG, or a compatible method static $use_random_int_functionality = null; if (is_null($use_random_int_functionality)) { $use_random_int_functionality = function_exists('random_int'); } if ($use_random_int_functionality) { try { $_max = ( 0 != $max ) ? $max : $max_random_number; // rand() can accept arguments in either order, PHP cannot. $_max = max($min, $_max); $_min = min($min, $_max); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.random_intFound $val = random_int($_min, $_max); if (false !== $val) { return abs(intval($val)); } else { // @phpstan-ignore-line $use_random_int_functionality = false; } } catch (Error $e) { $use_random_int_functionality = false; } catch (Exception $e) { $use_random_int_functionality = false; } } // Reset $rnd_value after 14 uses // 32(md5) + 40(sha1) + 40(sha1) / 8 = 14 random numbers from $rnd_value if (strlen($rnd_value) < 8) { static $seed = ''; $rnd_value = md5(uniqid(microtime() . mt_rand(), true) . $seed); $rnd_value .= sha1($rnd_value); $rnd_value .= sha1($rnd_value . $seed); $seed = md5($seed . $rnd_value); } // Take the first 8 digits for our value $value = substr($rnd_value, 0, 8); // Strip the first eight, leaving the remainder for the next call to rand(). $rnd_value = substr($rnd_value, 8); $value = abs(hexdec($value)); // Reduce the value to be within the min - max range if ($max != 0) { $value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 ); } return abs(intval($value)); } /** * Returns true if the class exists, false otherwise * * @param string $className Name of the class to check if it exists * @param boolean $autoload Parameter that will be passed to class_exists as second * * @return boolean */ public static function classExists($className, $autoload = true) { if (!class_exists($className, $autoload)) { return false; } if (function_exists("ini_get")) { $disabled = explode(',', ini_get('disable_classes')); return in_array($className, $disabled) ? false : true; } // We can only suppose that it exists, can't be 100% sure, but it's the best guess return true; } /** * Function phpinfo wrapper * * @see https://www.php.net/manual/en/function.phpinfo.php * * @param int $flags see phpinfo function flags * * @return bool Returns true on success or false on failure. */ public static function phpinfo($flags = INFO_ALL) { if (!function_exists('phpinfo')) { return false; } return phpinfo($flags); } /** * Checks if CURL is enabled * * @param bool $sslCheck Optional. Whether to check that the installed curl version supports SSL. * @param bool $multiCheck Optional. Whether to check that the installed curl version supports multi. * * @return bool True if CURL is enabled, false otherwise */ public static function isCurlEnabled($sslCheck = true, $multiCheck = false) { if (!function_exists('curl_init') || !function_exists('curl_exec') || !function_exists('curl_getinfo')) { return false; } // If needed, check that our installed curl version supports SSL if ($sslCheck) { if (($curl_version = curl_version()) === false) { return false; } if (!(CURL_VERSION_SSL & $curl_version['features'])) { return false; } } if ($multiCheck) { if (!function_exists('curl_multi_exec')) { return false; } } return true; } /** * Check if URL fopen is enabled * * @return bool */ public static function isUrlFopenEnabled() { if (!function_exists('ini_get')) { // is impossibile to know so is considered enabled return true; } return filter_var(ini_get('allow_url_fopen'), FILTER_VALIDATE_BOOLEAN); } /** * Set whether a client disconnect should abort script execution * * @param bool|null $enable true to enable, false to disable * * @return bool|int false if function can't be run, otherwise the value of the setting as integer before running this function */ public static function ignoreUserAbort($enable) { if (!function_exists('ignore_user_abort') || !is_callable('ignore_user_abort')) { return false; } return ignore_user_abort($enable); } }