[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |