Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   1  <?php
   2  /**
   3   * The Horde_Util:: class provides generally useful methods.
   4   *
   5   * Copyright 1999-2017 Horde LLC (http://www.horde.org/)
   6   *
   7   * See the enclosed file LICENSE for license information (LGPL). If you
   8   * did not receive this file, see http://www.horde.org/licenses/lgpl21.
   9   *
  10   * @author   Chuck Hagenbuch <chuck@horde.org>
  11   * @author   Jon Parise <jon@horde.org>
  12   * @category Horde
  13   * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
  14   * @package  Util
  15   */
  16  class Horde_Util
  17  {
  18      /**
  19       * A list of random patterns to use for overwriting purposes.
  20       * See http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html.
  21       * We save the random overwrites for efficiency reasons.
  22       *
  23       * @var array
  24       */
  25      public static $patterns = array(
  26          "\x55", "\xaa", "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49",
  27          "\x00", "\x11", "\x22", "\x33", "\x44", "\x55", "\x66", "\x77",
  28          "\x88", "\x99", "\xaa", "\xbb", "\xcc", "\xdd", "\xee", "\xff",
  29          "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49", "\x6d\xb6\xdb",
  30          "\xb6\xdb\x6d", "\xdb\x6d\xb6"
  31      );
  32  
  33      /**
  34       * Are magic quotes in use?
  35       *
  36       * @var boolean
  37       */
  38      protected static $_magicquotes = null;
  39  
  40      /**
  41       * Data used to determine shutdown deletion.
  42       *
  43       * @var array
  44       */
  45      protected static $_shutdowndata = array(
  46          'paths' => array(),
  47          'secure' => array()
  48      );
  49  
  50      /**
  51       * Has the shutdown method been registered?
  52       *
  53       * @var boolean
  54       */
  55      protected static $_shutdownreg = false;
  56  
  57      /**
  58       * Cache for extensionExists().
  59       *
  60       * @var array
  61       */
  62      protected static $_cache = array();
  63  
  64      /**
  65       * Checks to see if a value has been set by the script and not by GET,
  66       * POST, or cookie input. The value being checked MUST be in the global
  67       * scope.
  68       *
  69       * @param string $varname  The variable name to check.
  70       * @param mixed $default   Default value if the variable isn't present
  71       *                         or was specified by the user. Defaults to null.
  72       *
  73       * @return mixed  $default if the var is in user input or not present,
  74       *                the variable value otherwise.
  75       */
  76      public static function nonInputVar($varname, $default = null)
  77      {
  78          return (isset($_GET[$varname]) || isset($_POST[$varname]) || isset($_COOKIE[$varname]))
  79              ? $default
  80              : (isset($GLOBALS[$varname]) ? $GLOBALS[$varname] : $default);
  81      }
  82  
  83      /**
  84       * Returns a hidden form input containing the session name and id.
  85       *
  86       * @param boolean $append_session  0 = only if needed, 1 = always.
  87       *
  88       * @return string  The hidden form input, if needed/requested.
  89       */
  90      public static function formInput($append_session = 0)
  91      {
  92          return (($append_session == 1) || !isset($_COOKIE[session_name()]))
  93              ? '<input type="hidden" name="' . htmlspecialchars(session_name()) . '" value="' . htmlspecialchars(session_id()) . "\" />\n"
  94              : '';
  95      }
  96  
  97      /**
  98       * Prints a hidden form input containing the session name and id.
  99       *
 100       * @param boolean $append_session  0 = only if needed, 1 = always.
 101       */
 102      public static function pformInput($append_session = 0)
 103      {
 104          echo self::formInput($append_session);
 105      }
 106  
 107      /**
 108       * If magic_quotes_gpc is in use, run stripslashes() on $var.
 109       *
 110       * @param mixed $var  The string, or an array of strings, to un-quote.
 111       *
 112       * @return mixed  $var, minus any magic quotes.
 113       */
 114      public static function dispelMagicQuotes($var)
 115      {
 116          if (is_null(self::$_magicquotes)) {
 117              self::$_magicquotes = get_magic_quotes_gpc();
 118          }
 119  
 120          if (self::$_magicquotes) {
 121              $var = is_array($var)
 122                  ? array_map(array(__CLASS__, 'dispelMagicQuotes'), $var)
 123                  : stripslashes($var);
 124          }
 125  
 126          return $var;
 127      }
 128  
 129      /**
 130       * Gets a form variable from GET or POST data, stripped of magic quotes if
 131       * necessary. If the variable is somehow set in both the GET data and the
 132       * POST data, the value from the POST data will be returned and the GET
 133       * value will be ignored.
 134       *
 135       * @param string $var      The name of the form variable to look for.
 136       * @param string $default  The value to return if the variable is not
 137       *                         there.
 138       *
 139       * @return string  The cleaned form variable, or $default.
 140       */
 141      public static function getFormData($var, $default = null)
 142      {
 143          return (($val = self::getPost($var)) !== null)
 144              ? $val
 145              : self::getGet($var, $default);
 146      }
 147  
 148      /**
 149       * Gets a form variable from GET data, stripped of magic quotes if
 150       * necessary. This function will NOT return a POST variable.
 151       *
 152       * @param string $var      The name of the form variable to look for.
 153       * @param string $default  The value to return if the variable is not
 154       *                         there.
 155       *
 156       * @return string  The cleaned form variable, or $default.
 157       */
 158      public static function getGet($var, $default = null)
 159      {
 160          return (isset($_GET[$var]))
 161              ? self::dispelMagicQuotes($_GET[$var])
 162              : $default;
 163      }
 164  
 165      /**
 166       * Gets a form variable from POST data, stripped of magic quotes if
 167       * necessary. This function will NOT return a GET variable.
 168       *
 169       * @param string $var      The name of the form variable to look for.
 170       * @param string $default  The value to return if the variable is not
 171       *                         there.
 172       *
 173       * @return string  The cleaned form variable, or $default.
 174       */
 175      public static function getPost($var, $default = null)
 176      {
 177          return (isset($_POST[$var]))
 178              ? self::dispelMagicQuotes($_POST[$var])
 179              : $default;
 180      }
 181  
 182      /**
 183       * Creates a temporary filename for the lifetime of the script, and
 184       * (optionally) registers it to be deleted at request shutdown.
 185       *
 186       * @param string $prefix   Prefix to make the temporary name more
 187       *                         recognizable.
 188       * @param boolean $delete  Delete the file at the end of the request?
 189       * @param string $dir      Directory to create the temporary file in.
 190       * @param boolean $secure  If deleting the file, should we securely delete
 191       *                         the file by overwriting it with random data?
 192       *
 193       * @return string   Returns the full path-name to the temporary file.
 194       *                  Returns false if a temp file could not be created.
 195       */
 196      public static function getTempFile($prefix = '', $delete = true, $dir = '',
 197                                         $secure = false)
 198      {
 199          $tempDir = (empty($dir) || !is_dir($dir))
 200              ? sys_get_temp_dir()
 201              : $dir;
 202  
 203          $tempFile = tempnam($tempDir, $prefix);
 204  
 205          // If the file was created, then register it for deletion and return.
 206          if (empty($tempFile)) {
 207              return false;
 208          }
 209  
 210          if ($delete) {
 211              self::deleteAtShutdown($tempFile, true, $secure);
 212          }
 213  
 214          return $tempFile;
 215      }
 216  
 217      /**
 218       * Creates a temporary filename with a specific extension for the lifetime
 219       * of the script, and (optionally) registers it to be deleted at request
 220       * shutdown.
 221       *
 222       * @param string $extension  The file extension to use.
 223       * @param string $prefix     Prefix to make the temporary name more
 224       *                           recognizable.
 225       * @param boolean $delete    Delete the file at the end of the request?
 226       * @param string $dir        Directory to create the temporary file in.
 227       * @param boolean $secure    If deleting file, should we securely delete
 228       *                           the file by overwriting it with random data?
 229       *
 230       * @return string   Returns the full path-name to the temporary file.
 231       *                  Returns false if a temporary file could not be created.
 232       */
 233      public static function getTempFileWithExtension($extension = '.tmp',
 234                                                      $prefix = '',
 235                                                      $delete = true, $dir = '',
 236                                                      $secure = false)
 237      {
 238          $tempDir = (empty($dir) || !is_dir($dir))
 239              ? sys_get_temp_dir()
 240              : $dir;
 241  
 242          if (empty($tempDir)) {
 243              return false;
 244          }
 245  
 246          $windows = substr(PHP_OS, 0, 3) == 'WIN';
 247          $tries = 1;
 248          do {
 249              // Get a known, unique temporary file name.
 250              $sysFileName = tempnam($tempDir, $prefix);
 251              if ($sysFileName === false) {
 252                  return false;
 253              }
 254  
 255              // tack on the extension
 256              $tmpFileName = $sysFileName . $extension;
 257              if ($sysFileName == $tmpFileName) {
 258                  return $sysFileName;
 259              }
 260  
 261              // Move or point the created temporary file to the full filename
 262              // with extension. These calls fail if the new name already
 263              // exists.
 264              $fileCreated = ($windows ? @rename($sysFileName, $tmpFileName) : @link($sysFileName, $tmpFileName));
 265              if ($fileCreated) {
 266                  if (!$windows) {
 267                      unlink($sysFileName);
 268                  }
 269  
 270                  if ($delete) {
 271                      self::deleteAtShutdown($tmpFileName, true, $secure);
 272                  }
 273  
 274                  return $tmpFileName;
 275              }
 276  
 277              unlink($sysFileName);
 278          } while (++$tries <= 5);
 279  
 280          return false;
 281      }
 282  
 283      /**
 284       * Creates a temporary directory in the system's temporary directory.
 285       *
 286       * @param boolean $delete   Delete the temporary directory at the end of
 287       *                          the request?
 288       * @param string $temp_dir  Use this temporary directory as the directory
 289       *                          where the temporary directory will be created.
 290       *
 291       * @return string  The pathname to the new temporary directory.
 292       *                 Returns false if directory not created.
 293       */
 294      public static function createTempDir($delete = true, $temp_dir = null)
 295      {
 296          if (is_null($temp_dir)) {
 297              $temp_dir = sys_get_temp_dir();
 298          }
 299  
 300          if (empty($temp_dir)) {
 301              return false;
 302          }
 303  
 304          /* Get the first 8 characters of a random string to use as a temporary
 305             directory name. */
 306          do {
 307              $new_dir = $temp_dir . '/' . substr(base_convert(uniqid(mt_rand()), 10, 36), 0, 8);
 308          } while (file_exists($new_dir));
 309  
 310          $old_umask = umask(0000);
 311          if (!mkdir($new_dir, 0700)) {
 312              $new_dir = false;
 313          } elseif ($delete) {
 314              self::deleteAtShutdown($new_dir);
 315          }
 316          umask($old_umask);
 317  
 318          return $new_dir;
 319      }
 320  
 321      /**
 322       * Returns the canonical path of the string.  Like PHP's built-in
 323       * realpath() except the directory need not exist on the local server.
 324       *
 325       * Algorithim loosely based on code from the Perl File::Spec::Unix module
 326       * (version 1.5).
 327       *
 328       * @param string $path  A file path.
 329       *
 330       * @return string  The canonicalized file path.
 331       */
 332      public static function realPath($path)
 333      {
 334          /* Standardize on UNIX directory separators. */
 335          if (!strncasecmp(PHP_OS, 'WIN', 3)) {
 336              $path = str_replace('\\', '/', $path);
 337          }
 338  
 339          /* xx////xx -> xx/xx
 340           * xx/././xx -> xx/xx */
 341          $path = preg_replace(array("|/+|", "@(/\.)+(/|\Z(?!\n))@"), array('/', '/'), $path);
 342  
 343          /* ./xx -> xx */
 344          if ($path != './') {
 345              $path = preg_replace("|^(\./)+|", '', $path);
 346          }
 347  
 348          /* /../../xx -> xx */
 349          $path = preg_replace("|^/(\.\./?)+|", '/', $path);
 350  
 351          /* xx/ -> xx */
 352          if ($path != '/') {
 353              $path = preg_replace("|/\Z(?!\n)|", '', $path);
 354          }
 355  
 356          /* /xx/.. -> / */
 357          while (strpos($path, '/..') !== false) {
 358              $path = preg_replace("|/[^/]+/\.\.|", '', $path);
 359          }
 360  
 361          return empty($path) ? '/' : $path;
 362      }
 363  
 364      /**
 365       * Removes given elements at request shutdown.
 366       *
 367       * If called with a filename will delete that file at request shutdown; if
 368       * called with a directory will remove that directory and all files in that
 369       * directory at request shutdown.
 370       *
 371       * If called with no arguments, return all elements to be deleted (this
 372       * should only be done by Horde_Util::_deleteAtShutdown()).
 373       *
 374       * The first time it is called, it initializes the array and registers
 375       * Horde_Util::_deleteAtShutdown() as a shutdown function - no need to do
 376       * so manually.
 377       *
 378       * The second parameter allows the unregistering of previously registered
 379       * elements.
 380       *
 381       * @param string $filename   The filename to be deleted at the end of the
 382       *                           request.
 383       * @param boolean $register  If true, then register the element for
 384       *                           deletion, otherwise, unregister it.
 385       * @param boolean $secure    If deleting file, should we securely delete
 386       *                           the file?
 387       */
 388      public static function deleteAtShutdown($filename, $register = true,
 389                                              $secure = false)
 390      {
 391          /* Initialization of variables and shutdown functions. */
 392          if (!self::$_shutdownreg) {
 393              register_shutdown_function(array(__CLASS__, 'shutdown'));
 394              self::$_shutdownreg = true;
 395          }
 396  
 397          $ptr = &self::$_shutdowndata;
 398          if ($register) {
 399              $ptr['paths'][$filename] = true;
 400              if ($secure) {
 401                  $ptr['secure'][$filename] = true;
 402              }
 403          } else {
 404              unset($ptr['paths'][$filename], $ptr['secure'][$filename]);
 405          }
 406      }
 407  
 408      /**
 409       * Deletes registered files at request shutdown.
 410       *
 411       * This function should never be called manually; it is registered as a
 412       * shutdown function by Horde_Util::deleteAtShutdown() and called
 413       * automatically at the end of the request.
 414       *
 415       * Contains code from gpg_functions.php.
 416       * Copyright 2002-2003 Braverock Ventures
 417       */
 418      public static function shutdown()
 419      {
 420          $ptr = &self::$_shutdowndata;
 421  
 422          foreach (array_keys($ptr['paths']) as $val) {
 423              if (@is_file($val)) {
 424                  self::_secureDelete($val);
 425                  continue;
 426              }
 427  
 428              try {
 429                  $it = new RecursiveIteratorIterator(
 430                      new RecursiveDirectoryIterator($val),
 431                      RecursiveIteratorIterator::CHILD_FIRST
 432                  );
 433              } catch (UnexpectedValueException $e) {
 434                  continue;
 435              }
 436  
 437              while ($it->valid()) {
 438                  if (!$it->isDot()) {
 439                      if ($it->isDir()) {
 440                          @rmdir($it->key());
 441                      } elseif ($it->isFile()) {
 442                          self::_secureDelete($it->key());
 443                      } else {
 444                          @unlink($it->key());
 445                      }
 446                  }
 447                  $it->next();
 448              }
 449  
 450              @rmdir($val);
 451          }
 452      }
 453  
 454      /**
 455       * Securely delete the file by overwriting the data with a random
 456       * string.
 457       *
 458       * @param string $file  Filename.
 459       */
 460      protected static function _secureDelete($file)
 461      {
 462          if (isset($ptr['secure'][$file])) {
 463              $filesize = filesize($file);
 464              $fp = fopen($file, 'r+');
 465              foreach (self::$patterns as $pattern) {
 466                  $pattern = substr(str_repeat($pattern, floor($filesize / strlen($pattern)) + 1), 0, $filesize);
 467                  fwrite($fp, $pattern);
 468                  fseek($fp, 0);
 469              }
 470              fclose($fp);
 471          }
 472  
 473          @unlink($file);
 474      }
 475  
 476      /**
 477       * Caches the result of extension_loaded() calls.
 478       *
 479       * @param string $ext  The extension name.
 480       *
 481       * @return boolean  Is the extension loaded?
 482       */
 483      public static function extensionExists($ext)
 484      {
 485          if (!isset(self::$_cache[$ext])) {
 486              self::$_cache[$ext] = extension_loaded($ext);
 487          }
 488  
 489          return self::$_cache[$ext];
 490      }
 491  
 492      /**
 493       * Tries to load a PHP extension, behaving correctly for all operating
 494       * systems.
 495       *
 496       * @param string $ext  The extension to load.
 497       *
 498       * @return boolean  True if the extension is now loaded, false if not.
 499       *                  True can mean that the extension was already loaded,
 500       *                  OR was loaded dynamically.
 501       */
 502      public static function loadExtension($ext)
 503      {
 504          /* If $ext is already loaded, our work is done. */
 505          if (self::extensionExists($ext)) {
 506              return true;
 507          }
 508  
 509          /* See if we can call dl() at all, by the current ini settings.
 510           * dl() has been removed in some PHP 5.3 SAPIs. */
 511          if ((ini_get('enable_dl') != 1) ||
 512              (ini_get('safe_mode') == 1) ||
 513              !function_exists('dl')) {
 514              return false;
 515          }
 516  
 517          if (!strncasecmp(PHP_OS, 'WIN', 3)) {
 518              $suffix = 'dll';
 519          } else {
 520              switch (PHP_OS) {
 521              case 'HP-UX':
 522                  $suffix = 'sl';
 523                  break;
 524  
 525              case 'AIX':
 526                  $suffix = 'a';
 527                  break;
 528  
 529              case 'OSX':
 530                  $suffix = 'bundle';
 531                  break;
 532  
 533              default:
 534                  $suffix = 'so';
 535              }
 536          }
 537  
 538          return dl($ext . '.' . $suffix) || dl('php_' . $ext . '.' . $suffix);
 539      }
 540  
 541      /**
 542       * Utility function to obtain PATH_INFO information.
 543       *
 544       * @return string  The PATH_INFO string.
 545       */
 546      public static function getPathInfo()
 547      {
 548          if (isset($_SERVER['PATH_INFO']) &&
 549              (strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') === false)) {
 550              return $_SERVER['PATH_INFO'];
 551          } elseif (isset($_SERVER['REQUEST_URI']) &&
 552                    isset($_SERVER['SCRIPT_NAME'])) {
 553              $search = Horde_String::common($_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']);
 554              if (substr($search, -1) == '/') {
 555                  $search = substr($search, 0, -1);
 556              }
 557              $search = array($search);
 558              if (!empty($_SERVER['QUERY_STRING'])) {
 559                  // We can't use QUERY_STRING directly because URL rewriting
 560                  // might add more parameters to the query string than those
 561                  // from the request URI.
 562                  $url = parse_url($_SERVER['REQUEST_URI']);
 563                  if (!empty($url['query'])) {
 564                      $search[] = '?' . $url['query'];
 565                  }
 566              }
 567              $path = str_replace($search, '', $_SERVER['REQUEST_URI']);
 568              if ($path == '/') {
 569                  $path = '';
 570              }
 571              return $path;
 572          }
 573  
 574          return '';
 575      }
 576  
 577  }