Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.
<?php
/**
 * The Horde_Util:: class provides generally useful methods.
 *
 * Copyright 1999-2017 Horde LLC (http://www.horde.org/)
 *
 * See the enclosed file LICENSE for license information (LGPL). If you
 * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 *
 * @author   Chuck Hagenbuch <chuck@horde.org>
 * @author   Jon Parise <jon@horde.org>
 * @category Horde
 * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 * @package  Util
 */
class Horde_Util
{
    /**
     * A list of random patterns to use for overwriting purposes.
     * See http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html.
     * We save the random overwrites for efficiency reasons.
     *
     * @var array
     */
    public static $patterns = array(
        "\x55", "\xaa", "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49",
        "\x00", "\x11", "\x22", "\x33", "\x44", "\x55", "\x66", "\x77",
        "\x88", "\x99", "\xaa", "\xbb", "\xcc", "\xdd", "\xee", "\xff",
        "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49", "\x6d\xb6\xdb",
        "\xb6\xdb\x6d", "\xdb\x6d\xb6"
    );

    /**
     * Are magic quotes in use?
     *
     * @var boolean
     */
    protected static $_magicquotes = null;

    /**
     * Data used to determine shutdown deletion.
     *
     * @var array
     */
    protected static $_shutdowndata = array(
        'paths' => array(),
        'secure' => array()
    );

    /**
     * Has the shutdown method been registered?
     *
     * @var boolean
     */
    protected static $_shutdownreg = false;

    /**
     * Cache for extensionExists().
     *
     * @var array
     */
    protected static $_cache = array();

    /**
     * Checks to see if a value has been set by the script and not by GET,
     * POST, or cookie input. The value being checked MUST be in the global
     * scope.
     *
     * @param string $varname  The variable name to check.
     * @param mixed $default   Default value if the variable isn't present
     *                         or was specified by the user. Defaults to null.
     *
     * @return mixed  $default if the var is in user input or not present,
     *                the variable value otherwise.
     */
    public static function nonInputVar($varname, $default = null)
    {
        return (isset($_GET[$varname]) || isset($_POST[$varname]) || isset($_COOKIE[$varname]))
            ? $default
            : (isset($GLOBALS[$varname]) ? $GLOBALS[$varname] : $default);
    }

    /**
     * Returns a hidden form input containing the session name and id.
     *
     * @param boolean $append_session  0 = only if needed, 1 = always.
     *
     * @return string  The hidden form input, if needed/requested.
     */
    public static function formInput($append_session = 0)
    {
        return (($append_session == 1) || !isset($_COOKIE[session_name()]))
            ? '<input type="hidden" name="' . htmlspecialchars(session_name()) . '" value="' . htmlspecialchars(session_id()) . "\" />\n"
            : '';
    }

    /**
     * Prints a hidden form input containing the session name and id.
     *
     * @param boolean $append_session  0 = only if needed, 1 = always.
     */
    public static function pformInput($append_session = 0)
    {
        echo self::formInput($append_session);
    }

    /**
     * If magic_quotes_gpc is in use, run stripslashes() on $var.
     *
     * @param mixed $var  The string, or an array of strings, to un-quote.
     *
     * @return mixed  $var, minus any magic quotes.
     */
    public static function dispelMagicQuotes($var)
    {
        if (is_null(self::$_magicquotes)) {
< self::$_magicquotes = get_magic_quotes_gpc();
> self::$_magicquotes = function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc();
} if (self::$_magicquotes) { $var = is_array($var) ? array_map(array(__CLASS__, 'dispelMagicQuotes'), $var) : stripslashes($var); } return $var; } /** * Gets a form variable from GET or POST data, stripped of magic quotes if * necessary. If the variable is somehow set in both the GET data and the * POST data, the value from the POST data will be returned and the GET * value will be ignored. * * @param string $var The name of the form variable to look for. * @param string $default The value to return if the variable is not * there. * * @return string The cleaned form variable, or $default. */ public static function getFormData($var, $default = null) { return (($val = self::getPost($var)) !== null) ? $val : self::getGet($var, $default); } /** * Gets a form variable from GET data, stripped of magic quotes if * necessary. This function will NOT return a POST variable. * * @param string $var The name of the form variable to look for. * @param string $default The value to return if the variable is not * there. * * @return string The cleaned form variable, or $default. */ public static function getGet($var, $default = null) { return (isset($_GET[$var])) ? self::dispelMagicQuotes($_GET[$var]) : $default; } /** * Gets a form variable from POST data, stripped of magic quotes if * necessary. This function will NOT return a GET variable. * * @param string $var The name of the form variable to look for. * @param string $default The value to return if the variable is not * there. * * @return string The cleaned form variable, or $default. */ public static function getPost($var, $default = null) { return (isset($_POST[$var])) ? self::dispelMagicQuotes($_POST[$var]) : $default; } /** * Creates a temporary filename for the lifetime of the script, and * (optionally) registers it to be deleted at request shutdown. * * @param string $prefix Prefix to make the temporary name more * recognizable. * @param boolean $delete Delete the file at the end of the request? * @param string $dir Directory to create the temporary file in. * @param boolean $secure If deleting the file, should we securely delete * the file by overwriting it with random data? * * @return string Returns the full path-name to the temporary file. * Returns false if a temp file could not be created. */ public static function getTempFile($prefix = '', $delete = true, $dir = '', $secure = false) { $tempDir = (empty($dir) || !is_dir($dir)) ? sys_get_temp_dir() : $dir; $tempFile = tempnam($tempDir, $prefix); // If the file was created, then register it for deletion and return. if (empty($tempFile)) { return false; } if ($delete) { self::deleteAtShutdown($tempFile, true, $secure); } return $tempFile; } /** * Creates a temporary filename with a specific extension for the lifetime * of the script, and (optionally) registers it to be deleted at request * shutdown. * * @param string $extension The file extension to use. * @param string $prefix Prefix to make the temporary name more * recognizable. * @param boolean $delete Delete the file at the end of the request? * @param string $dir Directory to create the temporary file in. * @param boolean $secure If deleting file, should we securely delete * the file by overwriting it with random data? * * @return string Returns the full path-name to the temporary file. * Returns false if a temporary file could not be created. */ public static function getTempFileWithExtension($extension = '.tmp', $prefix = '', $delete = true, $dir = '', $secure = false) { $tempDir = (empty($dir) || !is_dir($dir)) ? sys_get_temp_dir() : $dir; if (empty($tempDir)) { return false; } $windows = substr(PHP_OS, 0, 3) == 'WIN'; $tries = 1; do { // Get a known, unique temporary file name. $sysFileName = tempnam($tempDir, $prefix); if ($sysFileName === false) { return false; } // tack on the extension $tmpFileName = $sysFileName . $extension; if ($sysFileName == $tmpFileName) { return $sysFileName; } // Move or point the created temporary file to the full filename // with extension. These calls fail if the new name already // exists. $fileCreated = ($windows ? @rename($sysFileName, $tmpFileName) : @link($sysFileName, $tmpFileName)); if ($fileCreated) { if (!$windows) { unlink($sysFileName); } if ($delete) { self::deleteAtShutdown($tmpFileName, true, $secure); } return $tmpFileName; } unlink($sysFileName); } while (++$tries <= 5); return false; } /** * Creates a temporary directory in the system's temporary directory. * * @param boolean $delete Delete the temporary directory at the end of * the request? * @param string $temp_dir Use this temporary directory as the directory * where the temporary directory will be created. * * @return string The pathname to the new temporary directory. * Returns false if directory not created. */ public static function createTempDir($delete = true, $temp_dir = null) { if (is_null($temp_dir)) { $temp_dir = sys_get_temp_dir(); } if (empty($temp_dir)) { return false; } /* Get the first 8 characters of a random string to use as a temporary directory name. */ do {
< $new_dir = $temp_dir . '/' . substr(base_convert(uniqid(mt_rand()), 10, 36), 0, 8);
> $new_dir = $temp_dir . '/' . substr(base_convert(uniqid(mt_rand()), 16, 36), 0, 8);
} while (file_exists($new_dir)); $old_umask = umask(0000); if (!mkdir($new_dir, 0700)) { $new_dir = false; } elseif ($delete) { self::deleteAtShutdown($new_dir); } umask($old_umask); return $new_dir; } /** * Returns the canonical path of the string. Like PHP's built-in * realpath() except the directory need not exist on the local server. * * Algorithim loosely based on code from the Perl File::Spec::Unix module * (version 1.5). * * @param string $path A file path. * * @return string The canonicalized file path. */ public static function realPath($path) { /* Standardize on UNIX directory separators. */ if (!strncasecmp(PHP_OS, 'WIN', 3)) { $path = str_replace('\\', '/', $path); } /* xx////xx -> xx/xx * xx/././xx -> xx/xx */ $path = preg_replace(array("|/+|", "@(/\.)+(/|\Z(?!\n))@"), array('/', '/'), $path); /* ./xx -> xx */ if ($path != './') { $path = preg_replace("|^(\./)+|", '', $path); } /* /../../xx -> xx */ $path = preg_replace("|^/(\.\./?)+|", '/', $path); /* xx/ -> xx */ if ($path != '/') { $path = preg_replace("|/\Z(?!\n)|", '', $path); } /* /xx/.. -> / */ while (strpos($path, '/..') !== false) { $path = preg_replace("|/[^/]+/\.\.|", '', $path); } return empty($path) ? '/' : $path; } /** * Removes given elements at request shutdown. * * If called with a filename will delete that file at request shutdown; if * called with a directory will remove that directory and all files in that * directory at request shutdown. * * If called with no arguments, return all elements to be deleted (this * should only be done by Horde_Util::_deleteAtShutdown()). * * The first time it is called, it initializes the array and registers * Horde_Util::_deleteAtShutdown() as a shutdown function - no need to do * so manually. * * The second parameter allows the unregistering of previously registered * elements. * * @param string $filename The filename to be deleted at the end of the * request. * @param boolean $register If true, then register the element for * deletion, otherwise, unregister it. * @param boolean $secure If deleting file, should we securely delete * the file? */ public static function deleteAtShutdown($filename, $register = true, $secure = false) { /* Initialization of variables and shutdown functions. */ if (!self::$_shutdownreg) { register_shutdown_function(array(__CLASS__, 'shutdown')); self::$_shutdownreg = true; } $ptr = &self::$_shutdowndata; if ($register) { $ptr['paths'][$filename] = true; if ($secure) { $ptr['secure'][$filename] = true; } } else { unset($ptr['paths'][$filename], $ptr['secure'][$filename]); } } /** * Deletes registered files at request shutdown. * * This function should never be called manually; it is registered as a * shutdown function by Horde_Util::deleteAtShutdown() and called * automatically at the end of the request. * * Contains code from gpg_functions.php. * Copyright 2002-2003 Braverock Ventures */ public static function shutdown() { $ptr = &self::$_shutdowndata; foreach (array_keys($ptr['paths']) as $val) { if (@is_file($val)) { self::_secureDelete($val); continue; } try { $it = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($val), RecursiveIteratorIterator::CHILD_FIRST ); } catch (UnexpectedValueException $e) { continue; } while ($it->valid()) { if (!$it->isDot()) { if ($it->isDir()) { @rmdir($it->key()); } elseif ($it->isFile()) { self::_secureDelete($it->key()); } else { @unlink($it->key()); } } $it->next(); } @rmdir($val); } } /** * Securely delete the file by overwriting the data with a random * string. * * @param string $file Filename. */ protected static function _secureDelete($file) { if (isset($ptr['secure'][$file])) { $filesize = filesize($file); $fp = fopen($file, 'r+'); foreach (self::$patterns as $pattern) { $pattern = substr(str_repeat($pattern, floor($filesize / strlen($pattern)) + 1), 0, $filesize); fwrite($fp, $pattern); fseek($fp, 0); } fclose($fp); } @unlink($file); } /** * Caches the result of extension_loaded() calls. * * @param string $ext The extension name. * * @return boolean Is the extension loaded? */ public static function extensionExists($ext) { if (!isset(self::$_cache[$ext])) { self::$_cache[$ext] = extension_loaded($ext); } return self::$_cache[$ext]; } /** * Tries to load a PHP extension, behaving correctly for all operating * systems. * * @param string $ext The extension to load. * * @return boolean True if the extension is now loaded, false if not. * True can mean that the extension was already loaded, * OR was loaded dynamically. */ public static function loadExtension($ext) { /* If $ext is already loaded, our work is done. */ if (self::extensionExists($ext)) { return true; } /* See if we can call dl() at all, by the current ini settings. * dl() has been removed in some PHP 5.3 SAPIs. */ if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1) || !function_exists('dl')) { return false; } if (!strncasecmp(PHP_OS, 'WIN', 3)) { $suffix = 'dll'; } else { switch (PHP_OS) { case 'HP-UX': $suffix = 'sl'; break; case 'AIX': $suffix = 'a'; break; case 'OSX': $suffix = 'bundle'; break; default: $suffix = 'so'; } } return dl($ext . '.' . $suffix) || dl('php_' . $ext . '.' . $suffix); } /** * Utility function to obtain PATH_INFO information. * * @return string The PATH_INFO string. */ public static function getPathInfo() { if (isset($_SERVER['PATH_INFO']) && (strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') === false)) { return $_SERVER['PATH_INFO']; } elseif (isset($_SERVER['REQUEST_URI']) && isset($_SERVER['SCRIPT_NAME'])) { $search = Horde_String::common($_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']); if (substr($search, -1) == '/') { $search = substr($search, 0, -1); } $search = array($search); if (!empty($_SERVER['QUERY_STRING'])) { // We can't use QUERY_STRING directly because URL rewriting // might add more parameters to the query string than those // from the request URI. $url = parse_url($_SERVER['REQUEST_URI']); if (!empty($url['query'])) { $search[] = '?' . $url['query']; } } $path = str_replace($search, '', $_SERVER['REQUEST_URI']); if ($path == '/') { $path = ''; } return $path; } return ''; } }