Move file cache functions into CacheManager class (in CacheManager.php)
authorBrion Vibber <brion@users.mediawiki.org>
Sat, 2 Aug 2003 10:13:27 +0000 (10:13 +0000)
committerBrion Vibber <brion@users.mediawiki.org>
Sat, 2 Aug 2003 10:13:27 +0000 (10:13 +0000)
and add support for showing the cached version of a page if unable to
contact the database.

Also add 'Vary: Accept-Encoding' to headers for every output, just to
make sure.

includes/Article.php
includes/CacheManager.php [new file with mode: 0644]
includes/DatabaseFunctions.php
includes/OutputPage.php
languages/Language.php

index 7cbc9a5..580d75d 100644 (file)
@@ -1620,18 +1620,20 @@ $wgLang->recodeForEdit( $wpTextbox1 ) .
                return $text;
        }
 
-
        /* Caching functions */
        
-    function tryFileCache() {
+       function tryFileCache() {
+               global $wgTitle;
+               
                if($this->isFileCacheable()) {
-                       if($this->isFileCacheGood()) {
-                wfDebug( " tryFileCache() - about to load\n" );
-                               $this->loadFromFileCache();
+                       $cache = new CacheManager( $wgTitle );
+                       if($cache->isFileCacheGood( $this->mTouched )) {
+                               wfDebug( " tryFileCache() - about to load\n" );
+                               $cache->loadFromFileCache();
                                exit;
                        } else {
-                   wfDebug( " tryFileCache() - starting buffer\n" );                   
-                       ob_start( array(&$this, 'saveToFileCache' ) );
+                               wfDebug( " tryFileCache() - starting buffer\n" );                       
+                               ob_start( array(&$cache, 'saveToFileCache' ) );
                        }
                } else {
                        wfDebug( " tryFileCache() - not cacheable\n" );
@@ -1655,100 +1657,6 @@ $wgLang->recodeForEdit( $wpTextbox1 ) .
                        
        }
        
-       function fileCacheName() {
-               global $wgTitle, $wgFileCacheDirectory, $wgLang;
-               if( !$this->mFileCache ) {
-                       $hash = md5( $key = $wgTitle->getDbkey() );
-                       if( $wgTitle->getNamespace() )
-                               $key = $wgLang->getNsText( $wgTitle->getNamespace() ) . ":" . $key;
-                       $key = str_replace( ".", "%2E", urlencode( $key ) );
-                       $hash1 = substr( $hash, 0, 1 );
-                       $hash2 = substr( $hash, 0, 2 );
-                       $this->mFileCache = "{$wgFileCacheDirectory}/{$hash1}/{$hash2}/{$key}.html";
-                       wfDebug( " fileCacheName() - {$this->mFileCache}\n" );
-               }
-               return $this->mFileCache;
-       }
-
-       function isFileCacheGood() {
-               global $wgUser, $wgCacheEpoch;
-               if(!file_exists( $fn = $this->fileCacheName() ) ) return false;
-               $cachetime = wfUnix2Timestamp( filemtime( $fn ) );
-               $good = (( $this->mTouched <= $cachetime ) &&
-                       ($wgCacheEpoch <= $cachetime ));
-        wfDebug(" isFileCacheGood() - cachetime $cachetime, touched {$this->mTouched} epoch {$wgCacheEpoch}, good $good\n");
-               return $good;
-       }
-
-       function loadFromFileCache() {
-               global $wgUseGzip, $wgOut;
-               wfDebug(" loadFromFileCache()\n");
-               $filename=$this->fileCacheName();
-               $filenamegz = "{$filename}.gz";
-               $wgOut->sendCacheControl();
-               if( $wgUseGzip
-                       && wfClientAcceptsGzip()
-                       && file_exists( $filenamegz)
-                       && ( filemtime( $filenamegz ) >= filemtime( $filename ) ) ) {
-                       wfDebug("  sending gzip\n");
-                       header( "Content-Encoding: gzip" );
-                       header( "Vary: Accept-Encoding" );
-                       $filename = $filenamegz;
-               }
-               readfile( $filename );
-       }
-       
-       function saveToFileCache( $text ) {
-               global $wgUseGzip, $wgCompressByDefault;
-               if(strcmp($text,"") == 0) return "";
-               
-        wfDebug(" saveToFileCache()\n", false);
-               $filename=$this->fileCacheName();
-                $mydir2=substr($filename,0,strrpos($filename,"/")); # subdirectory level 2
-               $mydir1=substr($mydir2,0,strrpos($mydir2,"/")); # subdirectory level 1
-               if(!file_exists($mydir1)) { mkdir($mydir1,0775); } # create if necessary
-               if(!file_exists($mydir2)) { mkdir($mydir2,0775); }
-               
-               $f = fopen( $filename, "w" );
-               if($f) {
-                       $now = wfTimestampNow();
-                       fwrite( $f, str_replace( "</html>",
-                               "<!-- Cached $now -->\n</html>",
-                               $text ) );
-                       fclose( $f );
-                       if( $wgUseGzip and $wgCompressByDefault ) {
-                               $start = microtime();
-                               wfDebug("  saving gzip\n");
-                               $gzout = gzencode( str_replace( "</html>",
-                                               "<!-- Cached/compressed $now -->\n</html>",
-                                               $text ) );
-                               if( $gzout === false ) {
-                                       wfDebug("  failed to gzip compress, sending plaintext\n");
-                                       return $text;
-                               }
-                               if( $f = fopen( "{$filename}.gz", "w" ) ) {
-                                       fwrite( $f, $gzout );
-                                       fclose( $f );
-                                       $end = microtime();
-                                       
-                                       list($usec1, $sec1) = explode(" ",$start);
-                                       list($usec2, $sec2) = explode(" ",$end);
-                                       $interval = ((float)$usec2 + (float)$sec2) -
-                                                               ((float)$usec1 + (float)$sec1);
-                                       wfDebug("  saved gzip in $interval\n");
-                               } else {
-                                       wfDebug("  failed to write gzip, still sending\n" );
-                               }
-                               if(wfClientAcceptsGzip()) {
-                                       header( "Content-Encoding: gzip" );
-                                       header( "Vary: Accept-Encoding" );
-                                       wfDebug("  sending NEW gzip now...\n" );
-                                       return $gzout;
-                               }
-                       }
-               }
-               return $text;
-       }
 
 }
 
diff --git a/includes/CacheManager.php b/includes/CacheManager.php
new file mode 100644 (file)
index 0000000..f42c530
--- /dev/null
@@ -0,0 +1,142 @@
+<?
+
+include_once( "Title.php" );
+
+class CacheManager {
+       var $mTitle, $mFileCache;
+       
+       function CacheManager( &$title ) {
+               $this->mTitle =& $title;
+               $this->mFileCache = "";
+       }
+       
+       function fileCacheName() {
+               global $wgFileCacheDirectory, $wgLang;
+               if( !$this->mFileCache ) {
+                       $hash = md5( $key = $this->mTitle->getDbkey() );
+                       if( $this->mTitle->getNamespace() )
+                               $key = $wgLang->getNsText( $this->mTitle->getNamespace() ) . ":" . $key;
+                       $key = str_replace( ".", "%2E", urlencode( $key ) );
+                       
+                       $hash1 = substr( $hash, 0, 1 );
+                       $hash2 = substr( $hash, 0, 2 );
+                       $this->mFileCache = "{$wgFileCacheDirectory}/{$hash1}/{$hash2}/{$key}.html";
+                       
+                       if($this->useGzip())
+                               $this->mFileCache .= ".gz";
+                       
+                       wfDebug( " fileCacheName() - {$this->mFileCache}\n" );
+               }
+               return $this->mFileCache;
+       }
+
+       function isFileCached() {
+               return file_exists( $this->fileCacheName() );
+       }
+       
+       function fileCacheTime() {
+               return wfUnix2Timestamp( filemtime( $this->fileCacheName() ) );
+       }
+       
+       function isFileCacheGood( $timestamp ) {
+               global $wgUser, $wgCacheEpoch;
+               
+               if( !$this->isFileCached() ) return false;
+               
+               $cachetime = $this->fileCacheTime();
+               $good = (( $timestamp <= $cachetime ) &&
+                        ( $wgCacheEpoch <= $cachetime ));
+               
+               wfDebug(" isFileCacheGood() - cachetime $cachetime, touched {$this->mTouched} epoch {$wgCacheEpoch}, good $good\n");
+               return $good;
+       }
+
+       function useGzip() {
+               global $wgUseGzip;
+               return $wgUseGzip;
+       }
+       
+       /* In handy string packages */
+       function fetchRawText() {
+               return file_get_contents( $this->fileCacheName() );
+       }
+       
+       function fetchPageText() {
+               if( $this->useGzip() ) {
+                       /* Why is there no gzfile_get_contents() or gzdecode()? */
+                       return implode( "", gzfile( $this->fileCacheName() ) );
+               } else {
+                       return $this->fetchRawText();
+               }
+       }
+       
+       /* Working directory to/from output */
+       function loadFromFileCache() {
+               global $wgOut;
+               wfDebug(" loadFromFileCache()\n");
+               
+               $filename=$this->fileCacheName();
+               $wgOut->sendCacheControl();
+               
+               if( $this->useGzip() ) {
+                       if( wfClientAcceptsGzip() ) {
+                               header( "Content-Encoding: gzip" );
+                               readfile( $filename );
+                       } else {
+                               /* Send uncompressed */
+                               readgzfile( $filename );
+                       }
+               } else {
+                       readfile( $filename );
+               }
+       }
+       
+       function checkCacheDirs() {
+               $filename = $this->fileCacheName();
+                $mydir2=substr($filename,0,strrpos($filename,"/")); # subdirectory level 2
+               $mydir1=substr($mydir2,0,strrpos($mydir2,"/")); # subdirectory level 1
+               
+               if(!file_exists($mydir1)) { mkdir($mydir1,0775); } # create if necessary
+               if(!file_exists($mydir2)) { mkdir($mydir2,0775); }
+       }
+       
+       function saveToFileCache( $text ) {
+               if(strcmp($text,"") == 0) return "";
+               
+               wfDebug(" saveToFileCache()\n", false);
+               
+               $this->checkCacheDirs();
+               
+               $f = fopen( $this->fileCacheName(), "w" );
+               if($f) {
+                       $now = wfTimestampNow();
+                       if( $this->useGzip() ) {
+                               $rawtext = str_replace( "</html>",
+                                       "<!-- Cached/compressed $now -->\n</html>",
+                                       $text );
+                               $text = gzencode( $rawtext );
+                       } else {
+                               $text = str_replace( "</html>",
+                                       "<!-- Cached $now -->\n</html>",
+                                       $text );
+                       }
+                       fwrite( $f, $text );
+                       fclose( $f );
+                       if( $this->useGzip() ) {
+                               if( wfClientAcceptsGzip() ) {
+                                       header( "Content-Encoding: gzip" );
+                                       header( "Vary: Accept-Encoding" );
+                                       return $text;
+                               } else {
+                                       return $rawtext;
+                               }
+                       } else {
+                               return $text;
+                       }
+               }
+               return $text;
+       }
+
+}
+
+?>
index bcec86d..191a763 100644 (file)
@@ -1,6 +1,7 @@
 <?
 global $IP;
 include_once( "$IP/FulltextStoplist.php" );
+include_once( "$IP/CacheManager.php" );
 
 $wgLastDatabaseQuery = "";
 
@@ -24,23 +25,63 @@ function wfGetDB( $altuser = "", $altpassword = "" )
        }
 
        if ( ! $wgDBconnection ) {
-               $wgDBconnection = mysql_pconnect( $wgDBserver, $wgDBuser,
-                 $wgDBpassword ) or die( $noconn .
-               "\n<p><b>" . htmlspecialchars(mysql_error()) . "</b></p>\n" . $helpme );
+               @$wgDBconnection = mysql_pconnect( $wgDBserver, $wgDBuser, $wgDBpassword )
+                       or wfEmergencyAbort();
+               
                if( !mysql_select_db( $wgDBname, $wgDBconnection ) ) {
+                       /* Persistent connections may become stuck in an unusable state */
                        wfDebug( "Persistent connection is broken?\n", true );
                        
-                       $wgDBconnection = mysql_connect( $wgDBserver, $wgDBuser,
-                         $wgDBpassword ) or die( $noconn .
-                 "\n<p><b>" . htmlspecialchars(mysql_error()) . "</b> (tried non-p connect)</p>\n" . $helpme );
-                       mysql_select_db( $wgDBname, $wgDBconnection ) or die( $nodb .
-                         "\n<p><b>" . htmlspecialchars(mysql_error()) . "</b> (tried non-p connect)</p>\n" . $helpme );
-        }
+                       @$wgDBconnection = mysql_connect( $wgDBserver, $wgDBuser, $wgDBpassword )
+                               or wfEmergencyAbort();
+                       
+                       @mysql_select_db( $wgDBname, $wgDBconnection )
+                               or wfEmergencyAbort();
+               }
        }
        # mysql_ping( $wgDBconnection );
        return $wgDBconnection;
 }
 
+/* Call this function if we couldn't contact the database...
+   We'll try to use the cache to display something in the meantime */
+function wfEmergencyAbort( $msg = "" ) {
+       global $wgTitle, $wgUseFileCache, $title;
+       
+       if($msg == "") $msg = wfMsg( "noconnect" );
+       $text = $msg;
+
+       if($wgUseFileCache) {
+               if($wgTitle) {
+                       $t =& $wgTitle;
+               } else {
+                       if($title) {
+                               $t = Title::newFromURL( $title );
+                       } else {
+                               $t = Title::newFromText( wfMsg("mainpage") );
+                       }
+               }
+
+               $cache = new CacheManager( $t );
+               if( $cache->isFileCached() ) {
+                       $msg = "<p style='color: red'><b>$msg<br>\n" .
+                               wfMsg( "cachederror" ) . "</b></p>\n";
+                       
+                       $tag = "<div id='article'>";
+                       $text = str_replace(
+                               $tag,
+                               $tag . $msg,
+                               $cache->fetchPageText() );
+               }
+       }
+       
+       /* Don't cache error pages!  They cause no end of trouble... */
+       header( "Cache-control: none" );
+       header( "Pragma: nocache" );
+       echo $text;
+       exit;
+}
+
 function wfQuery( $sql, $fname = "" )
 {
        global $wgLastDatabaseQuery, $wgOut;
index a75865f..7f1948a 100644 (file)
@@ -323,11 +323,13 @@ class OutputPage {
                if( $this->mLastModified != "" ) {
                        wfDebug( "** private caching; {$this->mLastModified} **\n", false );
                        header( "Cache-Control: private, must-revalidate, max-age=0" );
+                       header( "Vary: Accept-Encoding" );
                        header( "Last-modified: {$this->mLastModified}" );
                } else {
                        wfDebug( "** no caching **\n", false );
                        header( "Cache-Control: no-cache" ); # Experimental - see below
                        header( "Pragma: no-cache" );
+                       header( "Vary: Accept-Encoding" );
                        header( "Last-modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
                }
                header( "Expires: Mon, 15 Jan 2001 00:00:00 GMT" ); # Cachers always validate the page!
index 16f4d73..3ae261e 100644 (file)
@@ -401,8 +401,9 @@ The last attempted database query was:
 \"$1\"
 from within function \"$2\".
 MySQL returned error \"$3: $4\".\n",
-"noconnect"            => "Could not connect to DB on $1",
+"noconnect"            => "Sorry! The wiki is experiencing some technical difficulties, and cannot contact the database server.",
 "nodb"                 => "Could not select database $1",
+"cachederror"          => "The following is a cached copy of the requested page, and may not be up to date.",
 "readonly"             => "Database locked",
 "enterlockreason" => "Enter a reason for the lock, including an estimate
 of when the lock will be released",