[ Index ]

PHP Cross Reference of phpBB-3.3.14-deutsch

title

Body

[close]

/phpbb/cache/driver/ -> file.php (source)

   1  <?php
   2  /**
   3  *
   4  * This file is part of the phpBB Forum Software package.
   5  *
   6  * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7  * @license GNU General Public License, version 2 (GPL-2.0)
   8  *
   9  * For full copyright and license information, please see
  10  * the docs/CREDITS.txt file.
  11  *
  12  */
  13  
  14  namespace phpbb\cache\driver;
  15  
  16  /**
  17  * ACM File Based Caching
  18  */
  19  class file extends \phpbb\cache\driver\base
  20  {
  21      var $var_expires = array();
  22  
  23      /**
  24       * @var    \phpbb\filesystem\filesystem_interface
  25       */
  26      protected $filesystem;
  27  
  28      /**
  29      * Set cache path
  30      *
  31      * @param string $cache_dir Define the path to the cache directory (default: $phpbb_root_path . 'cache/')
  32      */
  33  	function __construct($cache_dir = null)
  34      {
  35          global $phpbb_container;
  36  
  37          $this->cache_dir = !is_null($cache_dir) ? $cache_dir : $phpbb_container->getParameter('core.cache_dir');
  38          $this->filesystem = new \phpbb\filesystem\filesystem();
  39  
  40          if ($this->filesystem->is_writable(dirname($this->cache_dir)) && !is_dir($this->cache_dir))
  41          {
  42              mkdir($this->cache_dir, 0777, true);
  43          }
  44      }
  45  
  46      /**
  47      * {@inheritDoc}
  48      */
  49  	function load()
  50      {
  51          return $this->_read('data_global');
  52      }
  53  
  54      /**
  55      * {@inheritDoc}
  56      */
  57  	function unload()
  58      {
  59          parent::unload();
  60          unset($this->var_expires);
  61          $this->var_expires = array();
  62      }
  63  
  64      /**
  65      * {@inheritDoc}
  66      */
  67  	function save()
  68      {
  69          if (!$this->is_modified)
  70          {
  71              return;
  72          }
  73  
  74          global $phpEx;
  75  
  76          if (!$this->_write('data_global'))
  77          {
  78              // Now, this occurred how often? ... phew, just tell the user then...
  79              if (!$this->filesystem->is_writable($this->cache_dir))
  80              {
  81                  // We need to use die() here, because else we may encounter an infinite loop (the message handler calls $cache->unload())
  82                  die('Fatal: ' . $this->cache_dir . ' is NOT writable.');
  83                  exit;
  84              }
  85  
  86              die('Fatal: Not able to open ' . $this->cache_dir . 'data_global.' . $phpEx);
  87              exit;
  88          }
  89  
  90          $this->is_modified = false;
  91      }
  92  
  93      /**
  94      * {@inheritDoc}
  95      */
  96  	function tidy()
  97      {
  98          global $config, $phpEx;
  99  
 100          $dir = @opendir($this->cache_dir);
 101  
 102          if (!$dir)
 103          {
 104              return;
 105          }
 106  
 107          $time = time();
 108  
 109          while (($entry = readdir($dir)) !== false)
 110          {
 111              if (!preg_match('/^(sql_|data_(?!global))/', $entry))
 112              {
 113                  continue;
 114              }
 115  
 116              if (!($handle = @fopen($this->cache_dir . $entry, 'rb')))
 117              {
 118                  continue;
 119              }
 120  
 121              // Skip the PHP header
 122              fgets($handle);
 123  
 124              // Skip expiration
 125              $expires = (int) fgets($handle);
 126  
 127              fclose($handle);
 128  
 129              if ($time >= $expires)
 130              {
 131                  $this->remove_file($this->cache_dir . $entry);
 132              }
 133          }
 134          closedir($dir);
 135  
 136          if (file_exists($this->cache_dir . 'data_global.' . $phpEx))
 137          {
 138              if (!count($this->vars))
 139              {
 140                  $this->load();
 141              }
 142  
 143              foreach ($this->var_expires as $var_name => $expires)
 144              {
 145                  if ($time >= $expires)
 146                  {
 147                      $this->destroy($var_name);
 148                  }
 149              }
 150          }
 151  
 152          $config->set('cache_last_gc', time(), false);
 153      }
 154  
 155      /**
 156      * {@inheritDoc}
 157      */
 158  	function get($var_name)
 159      {
 160          if ($var_name[0] == '_')
 161          {
 162              if (!$this->_exists($var_name))
 163              {
 164                  return false;
 165              }
 166  
 167              return $this->_read('data' . $var_name);
 168          }
 169          else
 170          {
 171              return ($this->_exists($var_name)) ? $this->vars[$var_name] : false;
 172          }
 173      }
 174  
 175      /**
 176      * {@inheritDoc}
 177      */
 178  	function put($var_name, $var, $ttl = 31536000)
 179      {
 180          if ($var_name[0] == '_')
 181          {
 182              $this->_write('data' . $var_name, $var, time() + $ttl);
 183          }
 184          else
 185          {
 186              $this->vars[$var_name] = $var;
 187              $this->var_expires[$var_name] = time() + $ttl;
 188              $this->is_modified = true;
 189          }
 190      }
 191  
 192      /**
 193      * {@inheritDoc}
 194      */
 195  	function purge()
 196      {
 197          parent::purge();
 198          $this->var_expires = array();
 199      }
 200  
 201      /**
 202      * {@inheritDoc}
 203      */
 204  	function destroy($var_name, $table = '')
 205      {
 206          global $phpEx;
 207  
 208          if ($var_name == 'sql' && !empty($table))
 209          {
 210              if (!is_array($table))
 211              {
 212                  $table = array($table);
 213              }
 214  
 215              $dir = @opendir($this->cache_dir);
 216  
 217              if (!$dir)
 218              {
 219                  return;
 220              }
 221  
 222              while (($entry = readdir($dir)) !== false)
 223              {
 224                  if (strpos($entry, 'sql_') !== 0)
 225                  {
 226                      continue;
 227                  }
 228  
 229                  if (!($handle = @fopen($this->cache_dir . $entry, 'rb')))
 230                  {
 231                      continue;
 232                  }
 233  
 234                  // Skip the PHP header
 235                  fgets($handle);
 236  
 237                  // Skip expiration
 238                  fgets($handle);
 239  
 240                  // Grab the query, remove the LF
 241                  $query = substr(fgets($handle), 0, -1);
 242  
 243                  fclose($handle);
 244  
 245                  foreach ($table as $check_table)
 246                  {
 247                      // Better catch partial table names than no table names. ;)
 248                      if (strpos($query, $check_table) !== false)
 249                      {
 250                          $this->remove_file($this->cache_dir . $entry);
 251                          break;
 252                      }
 253                  }
 254              }
 255              closedir($dir);
 256  
 257              return;
 258          }
 259  
 260          if (!$this->_exists($var_name))
 261          {
 262              return;
 263          }
 264  
 265          if ($var_name[0] == '_')
 266          {
 267              $this->remove_file($this->cache_dir . 'data' . $var_name . ".$phpEx", true);
 268          }
 269          else if (isset($this->vars[$var_name]))
 270          {
 271              $this->is_modified = true;
 272              unset($this->vars[$var_name]);
 273              unset($this->var_expires[$var_name]);
 274  
 275              // We save here to let the following cache hits succeed
 276              $this->save();
 277          }
 278      }
 279  
 280      /**
 281      * {@inheritDoc}
 282      */
 283  	function _exists($var_name)
 284      {
 285          if ($var_name[0] == '_')
 286          {
 287              global $phpEx;
 288              $var_name = $this->clean_varname($var_name);
 289              return file_exists($this->cache_dir . 'data' . $var_name . ".$phpEx");
 290          }
 291          else
 292          {
 293              if (!count($this->vars))
 294              {
 295                  $this->load();
 296              }
 297  
 298              if (!isset($this->var_expires[$var_name]))
 299              {
 300                  return false;
 301              }
 302  
 303              return (time() > $this->var_expires[$var_name]) ? false : isset($this->vars[$var_name]);
 304          }
 305      }
 306  
 307      /**
 308      * {@inheritDoc}
 309      */
 310  	function sql_save(\phpbb\db\driver\driver_interface $db, $query, $query_result, $ttl)
 311      {
 312          // Remove extra spaces and tabs
 313          $query = preg_replace('/[\n\r\s\t]+/', ' ', $query);
 314  
 315          $query_id = md5($query);
 316          $this->sql_rowset[$query_id] = array();
 317          $this->sql_row_pointer[$query_id] = 0;
 318  
 319          while ($row = $db->sql_fetchrow($query_result))
 320          {
 321              $this->sql_rowset[$query_id][] = $row;
 322          }
 323          $db->sql_freeresult($query_result);
 324  
 325          if ($this->_write('sql_' . $query_id, $this->sql_rowset[$query_id], $ttl + time(), $query))
 326          {
 327              return $query_id;
 328          }
 329  
 330          return $query_result;
 331      }
 332  
 333      /**
 334       * Cleanup when loading invalid data global file
 335       *
 336       * @param string $file Filename
 337       * @param resource $handle
 338       *
 339       * @return void
 340       */
 341  	private function cleanup_invalid_data_global(string $file, $handle): void
 342      {
 343          if (is_resource($handle))
 344          {
 345              fclose($handle);
 346          }
 347  
 348          $this->vars = $this->var_expires = [];
 349          $this->is_modified = false;
 350  
 351          $this->remove_file($file);
 352      }
 353  
 354      /**
 355      * Read cached data from a specified file
 356      *
 357      * @access private
 358      * @param string $filename Filename to write
 359      * @return mixed False if an error was encountered, otherwise the data type of the cached data
 360      */
 361  	function _read($filename)
 362      {
 363          global $phpEx;
 364  
 365          $filename = $this->clean_varname($filename);
 366          $file = "{$this->cache_dir}$filename.$phpEx";
 367  
 368          $type = substr($filename, 0, strpos($filename, '_'));
 369  
 370          if (!file_exists($file))
 371          {
 372              return false;
 373          }
 374  
 375          if (!($handle = @fopen($file, 'rb')))
 376          {
 377              return false;
 378          }
 379  
 380          // Skip the PHP header
 381          fgets($handle);
 382  
 383          if ($filename == 'data_global')
 384          {
 385              $this->vars = $this->var_expires = array();
 386  
 387              $time = time();
 388  
 389              while (($expires = (int) fgets($handle)) && !feof($handle))
 390              {
 391                  // Number of bytes of data
 392                  $bytes = substr(fgets($handle), 0, -1);
 393  
 394                  if (!is_numeric($bytes) || ($bytes = (int) $bytes) === 0)
 395                  {
 396                      $this->cleanup_invalid_data_global($file, $handle);
 397  
 398                      return false;
 399                  }
 400  
 401                  if ($time >= $expires)
 402                  {
 403                      fseek($handle, $bytes, SEEK_CUR);
 404  
 405                      continue;
 406                  }
 407  
 408                  $var_name = substr(fgets($handle), 0, -1);
 409                  $data_length = $bytes - strlen($var_name);
 410  
 411                  if ($data_length <= 0)
 412                  {
 413                      $this->cleanup_invalid_data_global($file, $handle);
 414  
 415                      return false;
 416                  }
 417  
 418                  // Read the length of bytes that consists of data.
 419                  $data = fread($handle, $data_length);
 420                  $data = @unserialize($data);
 421  
 422                  // Don't use the data if it was invalid
 423                  if ($data !== false)
 424                  {
 425                      $this->vars[$var_name] = $data;
 426                      $this->var_expires[$var_name] = $expires;
 427                  }
 428  
 429                  // Absorb the LF
 430                  fgets($handle);
 431              }
 432  
 433              fclose($handle);
 434  
 435              $this->is_modified = false;
 436  
 437              return true;
 438          }
 439          else
 440          {
 441              $data = false;
 442              $line = 0;
 443  
 444              while (($buffer = fgets($handle)) && !feof($handle))
 445              {
 446                  $buffer = substr($buffer, 0, -1); // Remove the LF
 447  
 448                  // $buffer is only used to read integers
 449                  // if it is non numeric we have an invalid
 450                  // cache file, which we will now remove.
 451                  if (!is_numeric($buffer))
 452                  {
 453                      break;
 454                  }
 455  
 456                  if ($line == 0)
 457                  {
 458                      $expires = (int) $buffer;
 459  
 460                      if (time() >= $expires)
 461                      {
 462                          break;
 463                      }
 464  
 465                      if ($type == 'sql')
 466                      {
 467                          // Skip the query
 468                          fgets($handle);
 469                      }
 470                  }
 471                  else if ($line == 1)
 472                  {
 473                      $bytes = (int) $buffer;
 474  
 475                      // Never should have 0 bytes
 476                      if (!$bytes)
 477                      {
 478                          break;
 479                      }
 480  
 481                      // Grab the serialized data
 482                      $data = fread($handle, $bytes);
 483  
 484                      // Read 1 byte, to trigger EOF
 485                      fread($handle, 1);
 486  
 487                      if (!feof($handle))
 488                      {
 489                          // Somebody tampered with our data
 490                          $data = false;
 491                      }
 492                      break;
 493                  }
 494                  else
 495                  {
 496                      // Something went wrong
 497                      break;
 498                  }
 499                  $line++;
 500              }
 501              fclose($handle);
 502  
 503              // unserialize if we got some data
 504              $data = ($data !== false) ? @unserialize($data) : $data;
 505  
 506              if ($data === false)
 507              {
 508                  $this->remove_file($file);
 509                  return false;
 510              }
 511  
 512              return $data;
 513          }
 514      }
 515  
 516      /**
 517      * Write cache data to a specified file
 518      *
 519      * 'data_global' is a special case and the generated format is different for this file:
 520      * <code>
 521      * <?php exit; ?>
 522      * (expiration)
 523      * (length of var and serialised data)
 524      * (var)
 525      * (serialised data)
 526      * ... (repeat)
 527      * </code>
 528      *
 529      * The other files have a similar format:
 530      * <code>
 531      * <?php exit; ?>
 532      * (expiration)
 533      * (query) [SQL files only]
 534      * (length of serialised data)
 535      * (serialised data)
 536      * </code>
 537      *
 538      * @access private
 539      * @param string $filename Filename to write
 540      * @param mixed $data Data to store
 541      * @param int $expires Timestamp when the data expires
 542      * @param string $query Query when caching SQL queries
 543      * @return bool True if the file was successfully created, otherwise false
 544      */
 545  	function _write($filename, $data = null, $expires = 0, $query = '')
 546      {
 547          global $phpEx;
 548  
 549          $filename = $this->clean_varname($filename);
 550          $file = "{$this->cache_dir}$filename.$phpEx";
 551  
 552          $lock = new \phpbb\lock\flock($file);
 553          $lock->acquire();
 554  
 555          if ($handle = @fopen($file, 'wb'))
 556          {
 557              // File header
 558              fwrite($handle, '<' . '?php exit; ?' . '>');
 559  
 560              if ($filename == 'data_global')
 561              {
 562                  // Global data is a different format
 563                  foreach ($this->vars as $var => $data)
 564                  {
 565                      if (strpos($var, "\r") !== false || strpos($var, "\n") !== false)
 566                      {
 567                          // CR/LF would cause fgets() to read the cache file incorrectly
 568                          // do not cache test entries, they probably won't be read back
 569                          // the cache keys should really be alphanumeric with a few symbols.
 570                          continue;
 571                      }
 572                      $data = serialize($data);
 573  
 574                      // Write out the expiration time
 575                      fwrite($handle, "\n" . $this->var_expires[$var] . "\n");
 576  
 577                      // Length of the remaining data for this var (ignoring two LF's)
 578                      fwrite($handle, strlen($data . $var) . "\n");
 579                      fwrite($handle, $var . "\n");
 580                      fwrite($handle, $data);
 581                  }
 582              }
 583              else
 584              {
 585                  fwrite($handle, "\n" . $expires . "\n");
 586  
 587                  if (strpos($filename, 'sql_') === 0)
 588                  {
 589                      fwrite($handle, $query . "\n");
 590                  }
 591                  $data = serialize($data);
 592  
 593                  fwrite($handle, strlen($data) . "\n");
 594                  fwrite($handle, $data);
 595              }
 596  
 597              fclose($handle);
 598  
 599              if (function_exists('opcache_invalidate'))
 600              {
 601                  @opcache_invalidate($file);
 602              }
 603  
 604              try
 605              {
 606                  $this->filesystem->phpbb_chmod($file, \phpbb\filesystem\filesystem_interface::CHMOD_READ | \phpbb\filesystem\filesystem_interface::CHMOD_WRITE);
 607              }
 608              catch (\phpbb\filesystem\exception\filesystem_exception $e)
 609              {
 610                  // Do nothing
 611              }
 612  
 613              $return_value = true;
 614          }
 615          else
 616          {
 617              $return_value = false;
 618          }
 619  
 620          $lock->release();
 621  
 622          return $return_value;
 623      }
 624  
 625      /**
 626      * Replace slashes in the file name
 627      *
 628      * @param string $varname name of a cache variable
 629      * @return string $varname name that is safe to use as a filename
 630      */
 631  	protected function clean_varname($varname)
 632      {
 633          return str_replace(array('/', '\\'), '-', $varname);
 634      }
 635  }


Generated: Mon Nov 25 19:05:08 2024 Cross-referenced by PHPXref 0.7.1