Wikini

MiseEnCacheDesPages

PagePrincipale :: DerniersChangements :: DerniersCommentaires :: ParametresUtilisateur :: Vous êtes ec2-3-17-165-16.us-east-2.compute.amazonaws.com

Mise en cache des pages de WikiNi

Une brève discussion avait été proposée quant à la mise en cache des pages dans les SuggestionsRapiditeDeTraitement. Cette page est destinée à en discuter de manière plus détaillée, et surtout à en évaluer les performances. NB.: cette page ne concerne en rien la mise en cache des requêtes sql.

Le système de cache que je propose:

J'ai impléménté (en fait c'est surtout recopier le FormatterWakka? et le remagnier un peu) un formatter cache.php qui permet de formatter une page normalement sauf qu'il retourne le code php à sauver dans un fichier afin d'utiliser ce dernier directement plutôt que de formatter la page à chaque nouvel affichage. Le gain se fait uniquement côté php car la requête de lecture de la page dans la base de données se fait de façon automatisée bien avant d'avoir sélectionné le handler, or dans le cas présent, seul le HandlerShow est concerné. (cf ClasseWikiMethodeRun?)

Fonctionnement
Todo:

Changelog

Sources:
formatters/cache.php (je ne sais pas si je peux le passer sous GPL...)
<?php
/**
 * Fichier cache.php: formatter permettant de construire un fichier php
 * (basé sur le formatter wakka.php)
 * @version $Idv 0.0.1 2005-01-08 $
 * @copyright 2002, Hendrik Mans <hendrik@mans.de>
 * @copyright 2002, 2003 David DELON
 * @copyright 2002, 2003 Charles NEPOTE
 * @copyright 2002, 2003 Patrick PAUL
 * @copyright 2003  Eric DELORD
 * @copyright 2003  Eric FELDSTEIN
 * @copyright 2004  Jean Christophe ANDRÉ
 * @copyright 2005  Didier LOISEAU
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// This may look a bit strange, but all possible formatting tags have to be in a single regular expression for this to work correctly. Yup!
if (!function_exists("CacheCallback"))
{
    function 
CacheCallback($things)
    {
        
$thing $things[1];
        
$result '';

        static 
$oldIndentLevel 0;
        static 
$oldIndentLength 0;
        static 
$indentClosers = array();
        static 
$newIndentSpace = array();
        static 
$br 1;

        global 
$wiki;

        switch (
$thing)
        { 
            
// convert HTML thingies
            
case '<':
                return 
'&lt;';
            case 
'>':
                return 
'&gt;'
            
// styles
            // bold
            
case '**':
                static 
$bold 0;
                return (++
$bold '<strong>' '</strong>'); 
            
// italic
            
case '//':
                static 
$italic 0;
                return (++
$italic '<i>' '</i>'); 
            
// underlinue
            
case '__':
                static 
$underline 0;
                return (++
$underline '<u>' '</u>'); 
            
// monospace
            
case '##':
                static 
$monospace 0;
                return (++
$monospace '<tt>' '</tt>'); 
            
// Deleted
            
case '@@':
                static 
$deleted 0;
                return (++
$deleted '<span class="del">' '</span>'); 
            
// Inserted
            
case '££':
                static 
$inserted 0;
                return (++
$inserted '<span class="add">' '</span>'); 
            
// header level 5
            
case '==':
                static 
$l5 0;
                
$br 0;
                return (++
$l5 "<h5>" "</h5>\n"); 
            
// header level 4
            
case '===':
                static 
$l4 0;
                
$br 0;
                return (++
$l4 "<h4>" "</h4>\n"); 
            
// header level 3
            
case '====':
                static 
$l3 0;
                
$br 0;
                return (++
$l3 "<h3>" "</h3>\n"); 
            
// header level 2
            
case '=====':
                static 
$l2 0;
                
$br 0;
                return (++
$l2 "<h2>" "</h2>\n"); 
            
// header level 1
            
case '======':
                static 
$l1 0;
                
$br 0;
                return (++
$l1 "<h1>" "</h1>\n"); 
            
// forced line breaks
            
case '---':
                return 
"<br />\n";
            default: 
                
// urls
                
if (preg_match("/^([a-z]+:\/\/\S+?)([^[:alnum:]^\/])?$/"$thing$matches))
                { 
                    
// Retrieve url and transform it into valid HTML (htmlentities)
                    
$url htmlentities ($matches[1]);
                    if (!isset(
$matches[2])) $matches[2] = '';
                    return 
"<a href=\"$url\">$url</a>" $matches[2];
                } 
                
// escaped text
                
elseif (preg_match("/^\"\"(.*)\"\"$/s"$thing$matches))
                {
                    return 
str_replace(array('<?''?>''<%''%>'), array('&lt;?''?&gt;''&lt;%''%&gt;'), $matches[1]);
                } 
                
// code text
                
elseif (preg_match("/^\%\%(.*)\%\%$/s"$thing$matches))
                { 
                    
// check if a language has been specified
                    
$code $matches[1];
                    if (
preg_match("/^\((.*)\)(.*)$/s"$code$matches))
                    {
                        list(, 
$language$code) = $matches;
                    } 
                    else
                    {
                        
$language '';
                    } 
                    
// Select formatter for syntaxe hightlighting
                    
if (file_exists("formatters/coloration_" $language ".php"))
                    {
                        
$formatter "coloration_" $language;
                    } 
                    else
                    {
                        
$formatter "code";
                    } 

                    
$output "<div class=\"code\">";
                    
$output .= $wiki->Format(trim($code), $formatter);
                    
$output .= "</div>\n";

                    return 
$output;
                } 
                
// raw inclusion from another wiki
                // (regexp documentation : see "forced link" below)
                
elseif (preg_match("/^\[\[\|(\S*)(\s+(.+))?\]\]$/"$thing$matches))
                {
                    list (, 
$url, , $text) = $matches;
                    if (!
$text$text "404";
                    if (
$url)
                    {
                        
$url .= "/wakka.php?wiki=" $text "/raw";
                        return 
'<?php echo $this->Format($this->Format(\'' addslashes($url) . '\', "raw"), "wakka"); ?>';
                    } 
                    else
                    {
                        return 
"";
                    } 
                } 
                
// forced links
                // \S : any character that is not a whitespace character
                // \s : any whitespace character
                
elseif (preg_match("/^\[\[(\S*)(\s+(.+))?\]\]$/"$thing$matches))
                {
                    if (isset(
$matches[2]) && isset($matches[3]))
                    {
                        list (, 
$url, , $text) = $matches;
                    } 
                    else
                    {
                        list (, 
$url) = $matches;
                    } 
                    if (
$url)
                    {
                        if (
$url != ($url = (preg_replace("/@@|££|\[\[/"""$url))))$result "</span>";
                        if (!isset(
$text)) $text $url;
                        
$text preg_replace("/@@|££|\[\[/"""$text);
                        return 
$result '<?php echo $this->Link(\'' addslashes($url) . '\', "", \'' addcslashes($text'\\\'') . '\'); ?>';
                    } 
                    else
                    {
                        return 
"";
                    } 
                } 
                
// indented text
                
elseif ((preg_match("/\n(\t+|([ ]{1})+)(-|([[:alnum:]]+)\))?/s"$thing$matches)) || (preg_match("/^(\t+|([ ]{1})+)(-|([[:alnum:]]+)\))?/s"$thing$matches) && $brf 1))
                { 
                    
// new line
                    
if (isset($brf)) $br 0;
                    
$result .= ($br "<br />\n" ""); 
                    
// we definitely want no line break in this one.
                    
$br 0
                    
// find out which indent type we want
                    
if (isset($matches[3]))
                    {
                        
$newIndentType $matches[3];
                    } 
                    else
                    {
                        
$newIndentType '';
                    } 
                    if (!
$newIndentType)
                    {
                        
$opener "<div class=\"indent\">";
                        
$closer "</div>";
                        
$br 1;
                    } 
                    elseif (
$newIndentType == "-")
                    {
                        
$opener "<ul>\n";
                        
$closer "</li>\n</ul>\n";
                        
$li 1;
                    } 
                    else
                    {
                        
$opener "<ol type=\"" $matches[4] . "\">\n";
                        
$closer "</li>\n</ol>\n";
                        
$li 1;
                    } 
                    
// get new indent level
                    
if (strpos($matches[1], "\t")) $newIndentLevel strlen($matches[1]);
                    else
                    {
                        
$newIndentLevel $oldIndentLevel;
                        
$newIndentLength strlen($matches[1]);
                        if (
$newIndentLength $oldIndentLength)
                        {
                            
$newIndentLevel++;
                            
$newIndentSpace[$newIndentLength] = $newIndentLevel;
                        } 
                        elseif (
$newIndentLength $oldIndentLength)
                            
$newIndentLevel $newIndentSpace[$newIndentLength];
                    } 
                    
$op 0;
                    if (
$newIndentLevel $oldIndentLevel)
                    {
                        for (
$i 0$i $newIndentLevel $oldIndentLevel$i++)
                        {
                            
$result .= $opener;
                            
$op 1;
                            
array_push($indentClosers$closer);
                        } 
                    } 
                    elseif (
$newIndentLevel $oldIndentLevel)
                    {
                        for (
$i 0$i $oldIndentLevel $newIndentLevel$i++)
                        {
                            
$op 1;
                            
$result .= array_pop($indentClosers);
                            if (isset(
$oldIndentLevel) && isset($li)) $result .= "</li>\n";
                        } 
                    } 

                    if (isset(
$li) && $op$result .= "<li>";
                    elseif (isset(
$li))
                        
$result .= "</li>\n<li>";

                    
$oldIndentLevel $newIndentLevel;
                    
$oldIndentLength $newIndentLength;

                    return 
$result;
                } 
                
// new lines
                
elseif ($thing == "\n")
                { 
                    
// if we got here, there was no tab in the next line; this means that we can close all open indents.
                    
$c count($indentClosers);
                    for (
$i 0$i $c$i++)
                    {
                        
$result .= array_pop($indentClosers);
                        
$br 0;
                    } 
                    
$oldIndentLevel 0;
                    
$oldIndentLength 0;
                    
$newIndentSpace = array();

                    
$result .= ($br "<br />\n" "\n");
                    
$br 1;
                    return 
$result;
                } 
                
// events
                
elseif (preg_match("/^\{\{(.*?)\}\}$/s"$thing$matches))
                {
                    if (
$matches[1])
                        return 
'<?php echo $this->Action(\'' addcslashes($matches[1], '\\\'') . '\'); ?>';
                    else
                        return 
"{{}}";
                } 
                
// interwiki links!
                
elseif (preg_match("/^" WN_UPPER WN_CHAR "+[:](" WN_CHAR2 "*)$/s"$thing))
                {
                    return 
'<?php echo $this->Link(\'' addslashes($thing) . '\'); ?>';
                } 
                
// wiki links!
                
elseif (preg_match("/^" WN_UPPER WN_LOWER "+" WN_UPPER_NUM WN_CHAR "*$/s"$thing))
                {
                    return 
'<?php echo $this->Link(\'' addslashes($thing) . '\'); ?>';
                } 
                
// separators
                
else if (preg_match("/-{4,}/"$thing$matches))
                { 
                    
// TODO: This could probably be improved for situations where someone puts text on the same line as a separator.
                    // Which is a stupid thing to do anyway! HAW HAW! Ahem.
                    
$br 0;
                    return 
"<hr />\n";
                } 
                
// if we reach this point, it must have been an accident.
                
return $thing;
        } 
    } 
}
 
$text str_replace("\r"""$text);
$text chop($text) . "\n";
$text preg_replace_callback("/(\%\%.*?\%\%|" "\"\".*?\"\"|" "\[\[.*?\]\]|" "\b[[:lower:]]+:\/\/\S+|" "\*\*|\#\#|@@|££|__|<|>|\/\/|" "======|=====|====|===|==|" "-{4,}|---|" "\n(\t+|([ ]{1})+)(-|[[:alnum:]]+\))?|" "^(\t+|([ ]{1})+)(-|[[:alnum:]]+\))?|" "\{\{.*?\}\}|" "\b" WN_UPPER WN_CHAR "+[:](" WN_CHAR2 "*)\b|" "\b(" WN_UPPER WN_LOWER "+" WN_UPPER_NUM WN_CHAR "*)\b|" "\n)/ms""CacheCallback"$text); 
// we're cutting the last <br />
$text preg_replace('/<br \/>$/'''trim($text));
echo 
"<?php\nif (!defined('WIKINI_VERSION'))\n\texit('Acc&egrave;s direct interdit');\n?>$text";

?>

Modification à apporter au HandlerShow: remplacer la partie "display page" par:
<?php
        
// display page
        // si la cache est activée, on l'utilise:
        
if ($cachedir $this->GetConfigValue('pagecache_dir'))
        {
            
// si IncludeBuffered ne retourne pas false, il a su lire la page en cache
            
if (false !== $page $this->IncludeBuffered($file $this->page['id'] . '.php'falsenull$cachedir))
            {
                echo 
$page;
            } 
            
// sinon cela signifie que la page n'était pas encore en cache, il faut donc l'y mettre
            
else
            {
                
$fp fopen($cachedir $file'w+');
                
fputs($fp$this->Format($this->page['body'], 'cache'));
                
fclose($fp);
                
// inutile de reformatter la page maintenant qu'on a une version disponible en cache...
                
include $cachedir $file;
            } 
        } 
        
// la cache est désactivée
        
else echo $this->Format($this->page["body"], "wakka"); 
?>
Et voilà, n'oubliez pas d'ajouter la variable de configuration "pagecache_dir" et d'autoriser le répertoire en question en écriture et le tour est joué ;-) Il ne reste qu'à évaluer le gain de performances...

Exemple de version en cache d'une page: l'ActionBackLinks:
<?php
if (!defined('WIKINI_VERSION'))
    exit(
'Accès direct interdit');
?>Action permettant d'insérer la liste de toutes les pages faisant référence à la page courante. Dans cette présente page, {{backlinks}} donne ceci :<br />
<br />
<?php echo $this->Action('BackLinks'); ?><br />
<br />
Sur la page personnelle d'un utilisateur, cette action affichera aussi les pages dont il est le propriétaire ou le dernier modificateur.<br />
<br />
<h2> Paramètres </h2>

<br />
<h3> Paramètre "page" </h3>

Le paramètre "page" (<?php echo $this->Link('http://www.wikini.net'""'WikiNi'); ?> &gt;= 0.4.1) permet de spécifier une page différente de la page courante.<br />
Par exemple <tt>{{backlinks page="PagePrincipale"}}</tt><br />
Ce paramètre peut être utile, par exemple :<br />
<ul>
<li> pour améliorer le <?php echo $this->Link('TableauDeBordDeCeWiki'); ?> : il suffit d'ajouter la liste des pages liées à la page AFaire pour connaître les pages qui doivent faire l'objet d'un travail</li>
<li> pour consolider des données sur une seule page : par exemple la liste des pages liées aux pages EstUnHomme et EstUneFemme</li>
<li> autres ?</li>
</ul>

<br />
<h3> Paramètre "exclude" </h3>

Le paramètre "exclude" (<?php echo $this->Link('http://www.wikini.net'""'WikiNi'); ?> &gt;= 0.4.1) permet de spécifier des pages à exclure de la liste des pages qui ont pourtant un lien vers la page de référence.<br />
Il est en effet parfois génant d'afficher la totalité des pages faisant référence à une page. Par exemple, la page AFaire liste la page CharlesNepoteVeilleSurInternet alors que, non seulement cette information n'a pas d'intérêt mais elle pollue en outre la lecture de cette page. Autre exemple, une page MamiFeres a intérêt par exemple à lister CheVal et ElePhant mais pas nécessairement AniMaux qui est une catégorie supérieure. Je suggère donc la création d'un paramètre exclude destiné à exclure certaines pages : par exemple :<br />
<tt>{{backlinks exclude="AniMaux"}}</tt><br />
Le paramètre "exclude" peut contenir plusieurs noms de page séparés par des ";", par exemple : <tt>{{backlinks exclude="AniMaux; PagePrincipale"}}</tt><br />
<br />
<hr />

<?php echo $this->Action('trail toc="ListeDesActionsWikini"'); ?>

-- LordFarquaad
Il n'y a pas de commentaire sur cette page. [Afficher commentaires/formulaire]