Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.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