[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Cache/ -> Cache.php (source)

   1  <?php
   2  /**
   3   * Joomla! Content Management System
   4   *
   5   * @copyright  (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
   6   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   7   */
   8  
   9  namespace Joomla\CMS\Cache;
  10  
  11  \defined('JPATH_PLATFORM') or die;
  12  
  13  use Joomla\Application\Web\WebClient;
  14  use Joomla\CMS\Cache\Exception\CacheExceptionInterface;
  15  use Joomla\CMS\Factory;
  16  use Joomla\CMS\Filesystem\Path;
  17  use Joomla\CMS\Session\Session;
  18  
  19  /**
  20   * Joomla! Cache base object
  21   *
  22   * @since  1.7.0
  23   */
  24  class Cache
  25  {
  26      /**
  27       * Storage handler
  28       *
  29       * @var    CacheStorage[]
  30       * @since  1.7.0
  31       */
  32      public static $_handler = array();
  33  
  34      /**
  35       * Cache options
  36       *
  37       * @var    array
  38       * @since  1.7.0
  39       */
  40      public $_options;
  41  
  42      /**
  43       * Constructor
  44       *
  45       * @param   array  $options  Cache options
  46       *
  47       * @since   1.7.0
  48       */
  49  	public function __construct($options)
  50      {
  51          $app = Factory::getApplication();
  52  
  53          $this->_options = array(
  54              'cachebase'    => $app->get('cache_path', JPATH_CACHE),
  55              'lifetime'     => (int) $app->get('cachetime'),
  56              'language'     => $app->get('language', 'en-GB'),
  57              'storage'      => $app->get('cache_handler', ''),
  58              'defaultgroup' => 'default',
  59              'locking'      => true,
  60              'locktime'     => 15,
  61              'checkTime'    => true,
  62              'caching'      => ($app->get('caching') >= 1),
  63          );
  64  
  65          // Overwrite default options with given options
  66          foreach ($options as $option => $value)
  67          {
  68              if (isset($options[$option]) && $options[$option] !== '')
  69              {
  70                  $this->_options[$option] = $options[$option];
  71              }
  72          }
  73  
  74          if (empty($this->_options['storage']))
  75          {
  76              $this->setCaching(false);
  77          }
  78      }
  79  
  80      /**
  81       * Returns a reference to a cache adapter object, always creating it
  82       *
  83       * @param   string  $type     The cache object type to instantiate
  84       * @param   array   $options  The array of options
  85       *
  86       * @return  CacheController
  87       *
  88       * @since       1.7.0
  89       * @deprecated  5.0 Use the cache controller factory instead
  90       */
  91  	public static function getInstance($type = 'output', $options = array())
  92      {
  93          @trigger_error(
  94              sprintf(
  95                  '%s() is deprecated. The cache controller should be fetched from the factory.',
  96                  __METHOD__
  97              ),
  98              E_USER_DEPRECATED
  99          );
 100  
 101          return Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController($type, $options);
 102      }
 103  
 104      /**
 105       * Get the storage handlers
 106       *
 107       * @return  array
 108       *
 109       * @since   1.7.0
 110       */
 111  	public static function getStores()
 112      {
 113          $handlers = array();
 114  
 115          // Get an iterator and loop through the driver classes.
 116          $iterator = new \DirectoryIterator(__DIR__ . '/Storage');
 117  
 118          /** @type  $file  \DirectoryIterator */
 119          foreach ($iterator as $file)
 120          {
 121              $fileName = $file->getFilename();
 122  
 123              // Only load for php files.
 124              if (!$file->isFile() || $file->getExtension() !== 'php' || $fileName === 'CacheStorageHelper.php')
 125              {
 126                  continue;
 127              }
 128  
 129              // Derive the class name from the type.
 130              $class = str_ireplace('.php', '', __NAMESPACE__ . '\\Storage\\' . ucfirst(trim($fileName)));
 131  
 132              // If the class doesn't exist we have nothing left to do but look at the next type. We did our best.
 133              if (!class_exists($class))
 134              {
 135                  continue;
 136              }
 137  
 138              // Sweet!  Our class exists, so now we just need to know if it passes its test method.
 139              if ($class::isSupported())
 140              {
 141                  // Connector names should not have file extensions.
 142                  $handler = str_ireplace('Storage.php', '', $fileName);
 143                  $handler = str_ireplace('.php', '', $handler);
 144                  $handlers[] = strtolower($handler);
 145              }
 146          }
 147  
 148          return $handlers;
 149      }
 150  
 151      /**
 152       * Set caching enabled state
 153       *
 154       * @param   boolean  $enabled  True to enable caching
 155       *
 156       * @return  void
 157       *
 158       * @since   1.7.0
 159       */
 160  	public function setCaching($enabled)
 161      {
 162          $this->_options['caching'] = $enabled;
 163      }
 164  
 165      /**
 166       * Get caching state
 167       *
 168       * @return  boolean
 169       *
 170       * @since   1.7.0
 171       */
 172  	public function getCaching()
 173      {
 174          return $this->_options['caching'];
 175      }
 176  
 177      /**
 178       * Set cache lifetime
 179       *
 180       * @param   integer  $lt  Cache lifetime
 181       *
 182       * @return  void
 183       *
 184       * @since   1.7.0
 185       */
 186  	public function setLifeTime($lt)
 187      {
 188          $this->_options['lifetime'] = $lt;
 189      }
 190  
 191      /**
 192       * Check if the cache contains data stored by ID and group
 193       *
 194       * @param   string  $id     The cache data ID
 195       * @param   string  $group  The cache data group
 196       *
 197       * @return  boolean
 198       *
 199       * @since   3.7.0
 200       */
 201  	public function contains($id, $group = null)
 202      {
 203          if (!$this->getCaching())
 204          {
 205              return false;
 206          }
 207  
 208          // Get the default group
 209          $group = $group ?: $this->_options['defaultgroup'];
 210  
 211          return $this->_getStorage()->contains($id, $group);
 212      }
 213  
 214      /**
 215       * Get cached data by ID and group
 216       *
 217       * @param   string  $id     The cache data ID
 218       * @param   string  $group  The cache data group
 219       *
 220       * @return  mixed  Boolean false on failure or a cached data object
 221       *
 222       * @since   1.7.0
 223       */
 224  	public function get($id, $group = null)
 225      {
 226          if (!$this->getCaching())
 227          {
 228              return false;
 229          }
 230  
 231          // Get the default group
 232          $group = $group ?: $this->_options['defaultgroup'];
 233  
 234          return $this->_getStorage()->get($id, $group, $this->_options['checkTime']);
 235      }
 236  
 237      /**
 238       * Get a list of all cached data
 239       *
 240       * @return  mixed  Boolean false on failure or an object with a list of cache groups and data
 241       *
 242       * @since   1.7.0
 243       */
 244  	public function getAll()
 245      {
 246          if (!$this->getCaching())
 247          {
 248              return false;
 249          }
 250  
 251          return $this->_getStorage()->getAll();
 252      }
 253  
 254      /**
 255       * Store the cached data by ID and group
 256       *
 257       * @param   mixed   $data   The data to store
 258       * @param   string  $id     The cache data ID
 259       * @param   string  $group  The cache data group
 260       *
 261       * @return  boolean
 262       *
 263       * @since   1.7.0
 264       */
 265  	public function store($data, $id, $group = null)
 266      {
 267          if (!$this->getCaching())
 268          {
 269              return false;
 270          }
 271  
 272          // Get the default group
 273          $group = $group ?: $this->_options['defaultgroup'];
 274  
 275          // Get the storage and store the cached data
 276          return $this->_getStorage()->store($id, $group, $data);
 277      }
 278  
 279      /**
 280       * Remove a cached data entry by ID and group
 281       *
 282       * @param   string  $id     The cache data ID
 283       * @param   string  $group  The cache data group
 284       *
 285       * @return  boolean
 286       *
 287       * @since   1.7.0
 288       */
 289  	public function remove($id, $group = null)
 290      {
 291          // Get the default group
 292          $group = $group ?: $this->_options['defaultgroup'];
 293  
 294          try
 295          {
 296              return $this->_getStorage()->remove($id, $group);
 297          }
 298          catch (CacheExceptionInterface $e)
 299          {
 300              if (!$this->getCaching())
 301              {
 302                  return false;
 303              }
 304  
 305              throw $e;
 306          }
 307      }
 308  
 309      /**
 310       * Clean cache for a group given a mode.
 311       *
 312       * group mode    : cleans all cache in the group
 313       * notgroup mode : cleans all cache not in the group
 314       *
 315       * @param   string  $group  The cache data group
 316       * @param   string  $mode   The mode for cleaning cache [group|notgroup]
 317       *
 318       * @return  boolean  True on success, false otherwise
 319       *
 320       * @since   1.7.0
 321       */
 322  	public function clean($group = null, $mode = 'group')
 323      {
 324          // Get the default group
 325          $group = $group ?: $this->_options['defaultgroup'];
 326  
 327          try
 328          {
 329              return $this->_getStorage()->clean($group, $mode);
 330          }
 331          catch (CacheExceptionInterface $e)
 332          {
 333              if (!$this->getCaching())
 334              {
 335                  return false;
 336              }
 337  
 338              throw $e;
 339          }
 340      }
 341  
 342      /**
 343       * Garbage collect expired cache data
 344       *
 345       * @return  boolean
 346       *
 347       * @since   1.7.0
 348       */
 349      public function gc()
 350      {
 351          try
 352          {
 353              return $this->_getStorage()->gc();
 354          }
 355          catch (CacheExceptionInterface $e)
 356          {
 357              if (!$this->getCaching())
 358              {
 359                  return false;
 360              }
 361  
 362              throw $e;
 363          }
 364      }
 365  
 366      /**
 367       * Set lock flag on cached item
 368       *
 369       * @param   string  $id        The cache data ID
 370       * @param   string  $group     The cache data group
 371       * @param   string  $locktime  The default locktime for locking the cache.
 372       *
 373       * @return  \stdClass  Object with properties of lock and locklooped
 374       *
 375       * @since   1.7.0
 376       */
 377  	public function lock($id, $group = null, $locktime = null)
 378      {
 379          $returning = new \stdClass;
 380          $returning->locklooped = false;
 381  
 382          if (!$this->getCaching())
 383          {
 384              $returning->locked = false;
 385  
 386              return $returning;
 387          }
 388  
 389          // Get the default group
 390          $group = $group ?: $this->_options['defaultgroup'];
 391  
 392          // Get the default locktime
 393          $locktime = $locktime ?: $this->_options['locktime'];
 394  
 395          /*
 396           * Allow storage handlers to perform locking on their own
 397           * NOTE drivers with lock need also unlock or unlocking will fail because of false $id
 398           */
 399          $handler = $this->_getStorage();
 400  
 401          if ($this->_options['locking'] == true)
 402          {
 403              $locked = $handler->lock($id, $group, $locktime);
 404  
 405              if ($locked !== false)
 406              {
 407                  return $locked;
 408              }
 409          }
 410  
 411          // Fallback
 412          $curentlifetime = $this->_options['lifetime'];
 413  
 414          // Set lifetime to locktime for storing in children
 415          $this->_options['lifetime'] = $locktime;
 416  
 417          $looptime = $locktime * 10;
 418          $id2      = $id . '_lock';
 419  
 420          if ($this->_options['locking'] == true)
 421          {
 422              $data_lock = $handler->get($id2, $group, $this->_options['checkTime']);
 423          }
 424          else
 425          {
 426              $data_lock         = false;
 427              $returning->locked = false;
 428          }
 429  
 430          if ($data_lock !== false)
 431          {
 432              $lock_counter = 0;
 433  
 434              // Loop until you find that the lock has been released. That implies that data get from other thread has finished
 435              while ($data_lock !== false)
 436              {
 437                  if ($lock_counter > $looptime)
 438                  {
 439                      $returning->locked = false;
 440                      $returning->locklooped = true;
 441                      break;
 442                  }
 443  
 444                  usleep(100);
 445                  $data_lock = $handler->get($id2, $group, $this->_options['checkTime']);
 446                  $lock_counter++;
 447              }
 448          }
 449  
 450          if ($this->_options['locking'] == true)
 451          {
 452              $returning->locked = $handler->store($id2, $group, 1);
 453          }
 454  
 455          // Revert lifetime to previous one
 456          $this->_options['lifetime'] = $curentlifetime;
 457  
 458          return $returning;
 459      }
 460  
 461      /**
 462       * Unset lock flag on cached item
 463       *
 464       * @param   string  $id     The cache data ID
 465       * @param   string  $group  The cache data group
 466       *
 467       * @return  boolean
 468       *
 469       * @since   1.7.0
 470       */
 471  	public function unlock($id, $group = null)
 472      {
 473          if (!$this->getCaching())
 474          {
 475              return false;
 476          }
 477  
 478          // Get the default group
 479          $group = $group ?: $this->_options['defaultgroup'];
 480  
 481          // Allow handlers to perform unlocking on their own
 482          $handler = $this->_getStorage();
 483  
 484          $unlocked = $handler->unlock($id, $group);
 485  
 486          if ($unlocked !== false)
 487          {
 488              return $unlocked;
 489          }
 490  
 491          // Fallback
 492          return $handler->remove($id . '_lock', $group);
 493      }
 494  
 495      /**
 496       * Get the cache storage handler
 497       *
 498       * @return  CacheStorage
 499       *
 500       * @since   1.7.0
 501       */
 502      public function &_getStorage()
 503      {
 504          $hash = md5(serialize($this->_options));
 505  
 506          if (isset(self::$_handler[$hash]))
 507          {
 508              return self::$_handler[$hash];
 509          }
 510  
 511          self::$_handler[$hash] = CacheStorage::getInstance($this->_options['storage'], $this->_options);
 512  
 513          return self::$_handler[$hash];
 514      }
 515  
 516      /**
 517       * Perform workarounds on retrieved cached data
 518       *
 519       * @param   array   $data     Cached data
 520       * @param   array   $options  Array of options
 521       *
 522       * @return  string  Body of cached data
 523       *
 524       * @since   1.7.0
 525       */
 526  	public static function getWorkarounds($data, $options = array())
 527      {
 528          $app      = Factory::getApplication();
 529          $document = Factory::getDocument();
 530          $body     = null;
 531  
 532          // Get the document head out of the cache.
 533          if (isset($options['mergehead']) && $options['mergehead'] == 1 && isset($data['head']) && !empty($data['head'])
 534              && method_exists($document, 'mergeHeadData'))
 535          {
 536              $document->mergeHeadData($data['head']);
 537          }
 538          elseif (isset($data['head']) && method_exists($document, 'setHeadData'))
 539          {
 540              $document->setHeadData($data['head']);
 541          }
 542  
 543          // Get the document MIME encoding out of the cache
 544          if (isset($data['mime_encoding']))
 545          {
 546              $document->setMimeEncoding($data['mime_encoding'], true);
 547          }
 548  
 549          // If the pathway buffer is set in the cache data, get it.
 550          if (isset($data['pathway']) && \is_array($data['pathway']))
 551          {
 552              // Push the pathway data into the pathway object.
 553              $app->getPathway()->setPathway($data['pathway']);
 554          }
 555  
 556          // @todo check if the following is needed, seems like it should be in page cache
 557          // If a module buffer is set in the cache data, get it.
 558          if (isset($data['module']) && \is_array($data['module']))
 559          {
 560              // Iterate through the module positions and push them into the document buffer.
 561              foreach ($data['module'] as $name => $contents)
 562              {
 563                  $document->setBuffer($contents, 'module', $name);
 564              }
 565          }
 566  
 567          // Set cached headers.
 568          if (isset($data['headers']) && $data['headers'])
 569          {
 570              foreach ($data['headers'] as $header)
 571              {
 572                  $app->setHeader($header['name'], $header['value']);
 573              }
 574          }
 575  
 576          // The following code searches for a token in the cached page and replaces it with the proper token.
 577          if (isset($data['body']))
 578          {
 579              $token       = Session::getFormToken();
 580              $search      = '#<input type="hidden" name="[0-9a-f]{32}" value="1">#';
 581              $replacement = '<input type="hidden" name="' . $token . '" value="1">';
 582  
 583              $data['body'] = preg_replace($search, $replacement, $data['body']);
 584              $body         = $data['body'];
 585          }
 586  
 587          // Get the document body out of the cache.
 588          return $body;
 589      }
 590  
 591      /**
 592       * Create workarounds for data to be cached
 593       *
 594       * @param   string  $data     Cached data
 595       * @param   array   $options  Array of options
 596       *
 597       * @return  array  Data to be cached
 598       *
 599       * @since   1.7.0
 600       */
 601  	public static function setWorkarounds($data, $options = [])
 602      {
 603          $loptions = [
 604              'nopathway'  => 0,
 605              'nohead'     => 0,
 606              'nomodules'  => 0,
 607              'modulemode' => 0,
 608          ];
 609  
 610          if (isset($options['nopathway']))
 611          {
 612              $loptions['nopathway'] = $options['nopathway'];
 613          }
 614  
 615          if (isset($options['nohead']))
 616          {
 617              $loptions['nohead'] = $options['nohead'];
 618          }
 619  
 620          if (isset($options['nomodules']))
 621          {
 622              $loptions['nomodules'] = $options['nomodules'];
 623          }
 624  
 625          if (isset($options['modulemode']))
 626          {
 627              $loptions['modulemode'] = $options['modulemode'];
 628          }
 629  
 630          $app      = Factory::getApplication();
 631          $document = Factory::getDocument();
 632  
 633          if ($loptions['nomodules'] != 1)
 634          {
 635              // Get the modules buffer before component execution.
 636              $buffer1 = $document->getBuffer();
 637  
 638              if (!\is_array($buffer1))
 639              {
 640                  $buffer1 = [];
 641              }
 642  
 643              // Make sure the module buffer is an array.
 644              if (!isset($buffer1['module']) || !\is_array($buffer1['module']))
 645              {
 646                  $buffer1['module'] = [];
 647              }
 648          }
 649  
 650          // View body data
 651          $cached['body'] = $data;
 652  
 653          // Document head data
 654          if ($loptions['nohead'] != 1 && method_exists($document, 'getHeadData'))
 655          {
 656              if ($loptions['modulemode'] == 1)
 657              {
 658                  $headNow = $document->getHeadData();
 659                  $unset   = ['title', 'description', 'link', 'links', 'metaTags'];
 660  
 661                  foreach ($unset as $key)
 662                  {
 663                      unset($headNow[$key]);
 664                  }
 665  
 666                  // Sanitize empty data
 667                  foreach (\array_keys($headNow) as $key)
 668                  {
 669                      if (!isset($headNow[$key]) || $headNow[$key] === [])
 670                      {
 671                          unset($headNow[$key]);
 672                      }
 673                  }
 674  
 675                  $cached['head'] = $headNow;
 676              }
 677              else
 678              {
 679                  $cached['head'] = $document->getHeadData();
 680  
 681                  // Document MIME encoding
 682                  $cached['mime_encoding'] = $document->getMimeEncoding();
 683              }
 684          }
 685  
 686          // Pathway data
 687          if ($app->isClient('site') && $loptions['nopathway'] != 1)
 688          {
 689              $cached['pathway'] = $data['pathway'] ?? $app->getPathway()->getPathway();
 690          }
 691  
 692          if ($loptions['nomodules'] != 1)
 693          {
 694              // @todo Check if the following is needed, seems like it should be in page cache
 695              // Get the module buffer after component execution.
 696              $buffer2 = $document->getBuffer();
 697  
 698              if (!\is_array($buffer2))
 699              {
 700                  $buffer2 = [];
 701              }
 702  
 703              // Make sure the module buffer is an array.
 704              if (!isset($buffer2['module']) || !\is_array($buffer2['module']))
 705              {
 706                  $buffer2['module'] = [];
 707              }
 708  
 709              // Compare the second module buffer against the first buffer.
 710              $cached['module'] = array_diff_assoc($buffer2['module'], $buffer1['module']);
 711          }
 712  
 713          // Headers data
 714          if (isset($options['headers']) && $options['headers'])
 715          {
 716              $cached['headers'] = $app->getHeaders();
 717          }
 718  
 719          return $cached;
 720      }
 721  
 722      /**
 723       * Create a safe ID for cached data from URL parameters
 724       *
 725       * @return  string  MD5 encoded cache ID
 726       *
 727       * @since   1.7.0
 728       */
 729  	public static function makeId()
 730      {
 731          $app = Factory::getApplication();
 732  
 733          $registeredurlparams = new \stdClass;
 734  
 735          // Get url parameters set by plugins
 736          if (!empty($app->registeredurlparams))
 737          {
 738              $registeredurlparams = $app->registeredurlparams;
 739          }
 740  
 741          // Platform defaults
 742          $defaulturlparams = array(
 743              'format' => 'WORD',
 744              'option' => 'WORD',
 745              'view'   => 'WORD',
 746              'layout' => 'WORD',
 747              'tpl'    => 'CMD',
 748              'id'     => 'INT',
 749          );
 750  
 751          // Use platform defaults if parameter doesn't already exist.
 752          foreach ($defaulturlparams as $param => $type)
 753          {
 754              if (!property_exists($registeredurlparams, $param))
 755              {
 756                  $registeredurlparams->$param = $type;
 757              }
 758          }
 759  
 760          $safeuriaddon = new \stdClass;
 761  
 762          foreach ($registeredurlparams as $key => $value)
 763          {
 764              $safeuriaddon->$key = $app->input->get($key, null, $value);
 765          }
 766  
 767          return md5(serialize($safeuriaddon));
 768      }
 769  
 770      /**
 771       * Set a prefix cache key if device calls for separate caching
 772       *
 773       * @return  string
 774       *
 775       * @since   3.5
 776       */
 777  	public static function getPlatformPrefix()
 778      {
 779          // No prefix when Global Config is set to no platform specific prefix
 780          if (!Factory::getApplication()->get('cache_platformprefix', false))
 781          {
 782              return '';
 783          }
 784  
 785          $webclient = new WebClient;
 786  
 787          if ($webclient->mobile)
 788          {
 789              return 'M-';
 790          }
 791  
 792          return '';
 793      }
 794  
 795      /**
 796       * Add a directory where Cache should search for handlers. You may either pass a string or an array of directories.
 797       *
 798       * @param   array|string  $path  A path to search.
 799       *
 800       * @return  array   An array with directory elements
 801       *
 802       * @since   1.7.0
 803       */
 804  	public static function addIncludePath($path = '')
 805      {
 806          static $paths;
 807  
 808          if (!isset($paths))
 809          {
 810              $paths = array();
 811          }
 812  
 813          if (!empty($path) && !\in_array($path, $paths))
 814          {
 815              array_unshift($paths, Path::clean($path));
 816          }
 817  
 818          return $paths;
 819      }
 820  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer