Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  
   3  class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCache
   4  {
   5  
   6      /**
   7       * @param HTMLPurifier_Definition $def
   8       * @param HTMLPurifier_Config $config
   9       * @return int|bool
  10       */
  11      public function add($def, $config)
  12      {
  13          if (!$this->checkDefType($def)) {
  14              return;
  15          }
  16          $file = $this->generateFilePath($config);
  17          if (file_exists($file)) {
  18              return false;
  19          }
  20          if (!$this->_prepareDir($config)) {
  21              return false;
  22          }
  23          return $this->_write($file, serialize($def), $config);
  24      }
  25  
  26      /**
  27       * @param HTMLPurifier_Definition $def
  28       * @param HTMLPurifier_Config $config
  29       * @return int|bool
  30       */
  31      public function set($def, $config)
  32      {
  33          if (!$this->checkDefType($def)) {
  34              return;
  35          }
  36          $file = $this->generateFilePath($config);
  37          if (!$this->_prepareDir($config)) {
  38              return false;
  39          }
  40          return $this->_write($file, serialize($def), $config);
  41      }
  42  
  43      /**
  44       * @param HTMLPurifier_Definition $def
  45       * @param HTMLPurifier_Config $config
  46       * @return int|bool
  47       */
  48      public function replace($def, $config)
  49      {
  50          if (!$this->checkDefType($def)) {
  51              return;
  52          }
  53          $file = $this->generateFilePath($config);
  54          if (!file_exists($file)) {
  55              return false;
  56          }
  57          if (!$this->_prepareDir($config)) {
  58              return false;
  59          }
  60          return $this->_write($file, serialize($def), $config);
  61      }
  62  
  63      /**
  64       * @param HTMLPurifier_Config $config
  65       * @return bool|HTMLPurifier_Config
  66       */
  67      public function get($config)
  68      {
  69          $file = $this->generateFilePath($config);
  70          if (!file_exists($file)) {
  71              return false;
  72          }
  73          return unserialize(file_get_contents($file));
  74      }
  75  
  76      /**
  77       * @param HTMLPurifier_Config $config
  78       * @return bool
  79       */
  80      public function remove($config)
  81      {
  82          $file = $this->generateFilePath($config);
  83          if (!file_exists($file)) {
  84              return false;
  85          }
  86          return unlink($file);
  87      }
  88  
  89      /**
  90       * @param HTMLPurifier_Config $config
  91       * @return bool
  92       */
  93      public function flush($config)
  94      {
  95          if (!$this->_prepareDir($config)) {
  96              return false;
  97          }
  98          $dir = $this->generateDirectoryPath($config);
  99          $dh = opendir($dir);
 100          // Apparently, on some versions of PHP, readdir will return
 101          // an empty string if you pass an invalid argument to readdir.
 102          // So you need this test.  See #49.
 103          if (false === $dh) {
 104              return false;
 105          }
 106          while (false !== ($filename = readdir($dh))) {
 107              if (empty($filename)) {
 108                  continue;
 109              }
 110              if ($filename[0] === '.') {
 111                  continue;
 112              }
 113              unlink($dir . '/' . $filename);
 114          }
 115          closedir($dh);
 116          return true;
 117      }
 118  
 119      /**
 120       * @param HTMLPurifier_Config $config
 121       * @return bool
 122       */
 123      public function cleanup($config)
 124      {
 125          if (!$this->_prepareDir($config)) {
 126              return false;
 127          }
 128          $dir = $this->generateDirectoryPath($config);
 129          $dh = opendir($dir);
 130          // See #49 (and above).
 131          if (false === $dh) {
 132              return false;
 133          }
 134          while (false !== ($filename = readdir($dh))) {
 135              if (empty($filename)) {
 136                  continue;
 137              }
 138              if ($filename[0] === '.') {
 139                  continue;
 140              }
 141              $key = substr($filename, 0, strlen($filename) - 4);
 142              if ($this->isOld($key, $config)) {
 143                  unlink($dir . '/' . $filename);
 144              }
 145          }
 146          closedir($dh);
 147          return true;
 148      }
 149  
 150      /**
 151       * Generates the file path to the serial file corresponding to
 152       * the configuration and definition name
 153       * @param HTMLPurifier_Config $config
 154       * @return string
 155       * @todo Make protected
 156       */
 157      public function generateFilePath($config)
 158      {
 159          $key = $this->generateKey($config);
 160          return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
 161      }
 162  
 163      /**
 164       * Generates the path to the directory contain this cache's serial files
 165       * @param HTMLPurifier_Config $config
 166       * @return string
 167       * @note No trailing slash
 168       * @todo Make protected
 169       */
 170      public function generateDirectoryPath($config)
 171      {
 172          $base = $this->generateBaseDirectoryPath($config);
 173          return $base . '/' . $this->type;
 174      }
 175  
 176      /**
 177       * Generates path to base directory that contains all definition type
 178       * serials
 179       * @param HTMLPurifier_Config $config
 180       * @return mixed|string
 181       * @todo Make protected
 182       */
 183      public function generateBaseDirectoryPath($config)
 184      {
 185          $base = $config->get('Cache.SerializerPath');
 186          $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
 187          return $base;
 188      }
 189  
 190      /**
 191       * Convenience wrapper function for file_put_contents
 192       * @param string $file File name to write to
 193       * @param string $data Data to write into file
 194       * @param HTMLPurifier_Config $config
 195       * @return int|bool Number of bytes written if success, or false if failure.
 196       */
 197      private function _write($file, $data, $config)
 198      {
 199          $result = file_put_contents($file, $data);
 200          if ($result !== false) {
 201              // set permissions of the new file (no execute)
 202              $chmod = $config->get('Cache.SerializerPermissions');
 203              if ($chmod !== null) {
 204                  chmod($file, $chmod & 0666);
 205              }
 206          }
 207          return $result;
 208      }
 209  
 210      /**
 211       * Prepares the directory that this type stores the serials in
 212       * @param HTMLPurifier_Config $config
 213       * @return bool True if successful
 214       */
 215      private function _prepareDir($config)
 216      {
 217          $directory = $this->generateDirectoryPath($config);
 218          $chmod = $config->get('Cache.SerializerPermissions');
 219          if ($chmod === null) {
 220              if (!@mkdir($directory) && !is_dir($directory)) {
 221                  trigger_error(
 222                      'Could not create directory ' . $directory . '',
 223                      E_USER_WARNING
 224                  );
 225                  return false;
 226              }
 227              return true;
 228          }
 229          if (!is_dir($directory)) {
 230              $base = $this->generateBaseDirectoryPath($config);
 231              if (!is_dir($base)) {
 232                  trigger_error(
 233                      'Base directory ' . $base . ' does not exist,
 234                      please create or change using %Cache.SerializerPath',
 235                      E_USER_WARNING
 236                  );
 237                  return false;
 238              } elseif (!$this->_testPermissions($base, $chmod)) {
 239                  return false;
 240              }
 241              if (!@mkdir($directory, $chmod) && !is_dir($directory)) {
 242                  trigger_error(
 243                      'Could not create directory ' . $directory . '',
 244                      E_USER_WARNING
 245                  );
 246                  return false;
 247              }
 248              if (!$this->_testPermissions($directory, $chmod)) {
 249                  return false;
 250              }
 251          } elseif (!$this->_testPermissions($directory, $chmod)) {
 252              return false;
 253          }
 254          return true;
 255      }
 256  
 257      /**
 258       * Tests permissions on a directory and throws out friendly
 259       * error messages and attempts to chmod it itself if possible
 260       * @param string $dir Directory path
 261       * @param int $chmod Permissions
 262       * @return bool True if directory is writable
 263       */
 264      private function _testPermissions($dir, $chmod)
 265      {
 266          // early abort, if it is writable, everything is hunky-dory
 267          if (is_writable($dir)) {
 268              return true;
 269          }
 270          if (!is_dir($dir)) {
 271              // generally, you'll want to handle this beforehand
 272              // so a more specific error message can be given
 273              trigger_error(
 274                  'Directory ' . $dir . ' does not exist',
 275                  E_USER_WARNING
 276              );
 277              return false;
 278          }
 279          if (function_exists('posix_getuid') && $chmod !== null) {
 280              // POSIX system, we can give more specific advice
 281              if (fileowner($dir) === posix_getuid()) {
 282                  // we can chmod it ourselves
 283                  $chmod = $chmod | 0700;
 284                  if (chmod($dir, $chmod)) {
 285                      return true;
 286                  }
 287              } elseif (filegroup($dir) === posix_getgid()) {
 288                  $chmod = $chmod | 0070;
 289              } else {
 290                  // PHP's probably running as nobody, so we'll
 291                  // need to give global permissions
 292                  $chmod = $chmod | 0777;
 293              }
 294              trigger_error(
 295                  'Directory ' . $dir . ' not writable, ' .
 296                  'please chmod to ' . decoct($chmod),
 297                  E_USER_WARNING
 298              );
 299          } else {
 300              // generic error message
 301              trigger_error(
 302                  'Directory ' . $dir . ' not writable, ' .
 303                  'please alter file permissions',
 304                  E_USER_WARNING
 305              );
 306          }
 307          return false;
 308      }
 309  }
 310  
 311  // vim: et sw=4 sts=4