ParserCache.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. <?php
  2. /**
  3. * @ingroup Cache Parser
  4. * @todo document
  5. */
  6. class ParserCache {
  7. /**
  8. * Get an instance of this object
  9. */
  10. public static function &singleton() {
  11. static $instance;
  12. if ( !isset( $instance ) ) {
  13. global $parserMemc;
  14. $instance = new ParserCache( $parserMemc );
  15. }
  16. return $instance;
  17. }
  18. /**
  19. * Setup a cache pathway with a given back-end storage mechanism.
  20. * May be a memcached client or a BagOStuff derivative.
  21. *
  22. * @param object $memCached
  23. */
  24. function __construct( &$memCached ) {
  25. $this->mMemc =& $memCached;
  26. }
  27. function getKey( &$article, $popts ) {
  28. global $wgRequest;
  29. if( $popts instanceof User ) // It used to be getKey( &$article, &$user )
  30. $popts = ParserOptions::newFromUser( $popts );
  31. $user = $popts->mUser;
  32. $printable = ( $popts->getIsPrintable() ) ? '!printable=1' : '';
  33. $hash = $user->getPageRenderingHash();
  34. if( !$article->mTitle->quickUserCan( 'edit' ) ) {
  35. // section edit links are suppressed even if the user has them on
  36. $edit = '!edit=0';
  37. } else {
  38. $edit = '';
  39. }
  40. $pageid = $article->getID();
  41. $renderkey = (int)($wgRequest->getVal('action') == 'render');
  42. $key = wfMemcKey( 'pcache', 'idhash', "{$pageid}-{$renderkey}!{$hash}{$edit}{$printable}" );
  43. return $key;
  44. }
  45. function getETag( &$article, $popts ) {
  46. return 'W/"' . $this->getKey($article, $popts) . "--" . $article->mTouched. '"';
  47. }
  48. function get( &$article, $popts ) {
  49. global $wgCacheEpoch;
  50. $fname = 'ParserCache::get';
  51. wfProfileIn( $fname );
  52. $key = $this->getKey( $article, $popts );
  53. wfDebug( "Trying parser cache $key\n" );
  54. $value = $this->mMemc->get( $key );
  55. if ( is_object( $value ) ) {
  56. wfDebug( "Found.\n" );
  57. # Delete if article has changed since the cache was made
  58. $canCache = $article->checkTouched();
  59. $cacheTime = $value->getCacheTime();
  60. $touched = $article->mTouched;
  61. if ( !$canCache || $value->expired( $touched ) ) {
  62. if ( !$canCache ) {
  63. wfIncrStats( "pcache_miss_invalid" );
  64. wfDebug( "Invalid cached redirect, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" );
  65. } else {
  66. wfIncrStats( "pcache_miss_expired" );
  67. wfDebug( "Key expired, touched $touched, epoch $wgCacheEpoch, cached $cacheTime\n" );
  68. }
  69. $this->mMemc->delete( $key );
  70. $value = false;
  71. } else {
  72. if ( isset( $value->mTimestamp ) ) {
  73. $article->mTimestamp = $value->mTimestamp;
  74. }
  75. wfIncrStats( "pcache_hit" );
  76. }
  77. } else {
  78. wfDebug( "Parser cache miss.\n" );
  79. wfIncrStats( "pcache_miss_absent" );
  80. $value = false;
  81. }
  82. wfProfileOut( $fname );
  83. return $value;
  84. }
  85. function save( $parserOutput, &$article, $popts ){
  86. global $wgParserCacheExpireTime;
  87. $key = $this->getKey( $article, $popts );
  88. if( $parserOutput->getCacheTime() != -1 ) {
  89. $now = wfTimestampNow();
  90. $parserOutput->setCacheTime( $now );
  91. // Save the timestamp so that we don't have to load the revision row on view
  92. $parserOutput->mTimestamp = $article->getTimestamp();
  93. $parserOutput->mText .= "\n<!-- Saved in parser cache with key $key and timestamp $now -->\n";
  94. wfDebug( "Saved in parser cache with key $key and timestamp $now\n" );
  95. if( $parserOutput->containsOldMagic() ){
  96. $expire = 3600; # 1 hour
  97. } else {
  98. $expire = $wgParserCacheExpireTime;
  99. }
  100. $this->mMemc->set( $key, $parserOutput, $expire );
  101. } else {
  102. wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
  103. }
  104. }
  105. }