12

Coloration syntaxique des fichiers sources

Bien présenter des fichiers sources en les colorisant les rend plus lisibles et plus attractifs. Rien de plus facile avec GeSHi et un peu de code en PHP.

Téléchargez GeSHi - Generic Syntax Highlighter. Ouvrez l'archive geshi-*.bz2.

Organisez le contenu de votre site en créant à la racine du site les dossiers includes pour les fichiers inclus et library pour votre propre code. Copiez le répertoire geshi et le fichier geshi.php de l'archive dans /includes.

  1. /
    1. includes
      1. geshi
      2. geshi.php
    2. library
      1. prettyfile.php

Copiez le code suivant dans un fichier appelé prettyfile.php dans /library.

prettyfile.php définit deux fonctions : read_file et pretty_file. read_file lit tout un fichier, ou une partie d'un fichier, dans une chaîne de caractères. pretty_file retourne tout le contenu d'un fichier, ou d'une partie d'un fichier, mis en valeur pour un langage informatique donné.

  1. require_once 'geshi.php';

Le code commence par charger les fonctions de geshi.php. NOTE : Configurez votre site pour que les répertoires /includes et /library soient listés dans le chemin d'inclusion de PHP. Ajoutez les lignes suivantes au début de votre code à la racine du site :

define('ROOT_DIR', dirname(__FILE__));

set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'library');
set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'includes');
read_file
  1. function read_file($file, $startline=0, $endline=0) {
  2.     if ($startline or $endline) {
  3.         $lines=@file($file);
  4.  
  5.         if (false === $lines) {
  6.             return false;
  7.         }
  8.  
  9.         $offset=$startline ? $startline-1 : 0;
  10.  
  11.         if ($endline) {
  12.             $length=$startline ? $endline - $startline + 1 : $endline;
  13.             $lines = array_slice($lines, $offset, $length);
  14.         }
  15.         else {
  16.             $lines = array_slice($lines, $offset);
  17.         }
  18.  
  19.         $s=implode('', $lines);
  20.     }
  21.     else {
  22.         $s=@file_get_contents($file);
  23.  
  24.         if (false === $s) {
  25.             return false;
  26.         }
  27.     }
  28.  
  29.     $s=rtrim($s);
  30.  
  31.     return $s;
  32. }

Si les deux paramètres $startline et $endline valent 0, on lit tout le fichier avec file_get_contents.

Pour extraire une partie du fichier, on le lit avec file qui retourne toutes les lignes dans un tableau. Chaque ligne dans $lines inclut le caractère de fin de ligne, sauf éventuellement la dernière. $lines est réduit avec array_slice à la portion du fichier voulu. Si $startline vaut 0, les $endline premières lignes sont extraites. Si $endline vaut 0, toutes les lignes de $startline à la fin du fichier sont extraites. Si $startline et $endline sont différents de 0, les lignes entre $startline et $endline sont extraites. Le tableau obtenu est changé en une chaîne de caractères avec implode.

Enfin, les lignes vides en fin de texte sont supprimées.

read_file retourne les lignes lues à partir de $file dans une chaîne de caractères ou false en cas d'erreur.

pretty_file
  1. function pretty_file($file, $language, $startline=0, $endline=0) {
  2.     $s=read_file($file, $startline, $endline);
  3.  
  4.     if (!$s) {
  5.         return false;
  6.     }
  7.  
  8.     if (!$language) {
  9.         return $s;
  10.     }
  11.  
  12.     $output = false;
  13.  
  14.     switch ($language) {
  15.         case 'plain':
  16.             $s = preg_replace("/\]\=\>\n(\s+)/m", "] => ", $s);
  17.             $s = htmlentities($s, ENT_COMPAT, 'UTF-8');
  18.  
  19.             $output = '<pre class="plain">' . PHP_EOL . $s . '</pre>' . PHP_EOL;
  20.             break;
  21.         default:
  22.             $geshi = new GeSHi($s, $language);
  23.             $geshi->enable_classes(true);
  24.             $geshi->set_header_type(GESHI_HEADER_DIV);
  25.             $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
  26.             $geshi->start_line_numbers_at($startline > 0 ? $startline : 1);
  27.             $geshi->enable_keyword_links(false);
  28.             $geshi->set_tab_width(4);
  29.  
  30. //          echo '<pre>' . PHP_EOL .$geshi->get_stylesheet( ). '</pre>' . PHP_EOL;
  31.  
  32.             $output = $geshi->parse_code();
  33.  
  34.             if ($geshi->error()) {
  35.                 return false;
  36.             }
  37.     }
  38.  
  39.     return $output;
  40. }

pretty_file commence par appeler read_line pour lire $file de $startline à $endline. En cas d'erreur, elle retourne false.

Si $language n'est pas spécifié, le contenu de $file est retourné sans formatage. NOTE : Évitez de lire un fichier dont le contenu peut être interprété par un navigateur.

Si $language vaut 'plain', le contenu de $file est retourné dans un bloc <pre> avec l'attribut class à la valeur plain pour le CSS. L'indentation et les renvois à la ligne sont nettoyés et les entités HTML sont récrites avec htmlentities. Remarquez que le texte est censé avoir été encodé en UTF-8.

Toute autre valeur du paramètre $language fait appel à GeSHi. $geshi est initialisé avec les lignes à formater et le nom du langage informatique. Ensuite, avant d'appeler parse_code, l'objet est configuré de façon à obtenir le fichier en sortie le plus petit sans aucun code CSS.

pretty_file retourne $output ou false en cas d'erreur.

Écrivez un petit document qui testera pretty_file en se colorisant lui-même. Enregistrez le code suivant dans un fichier appelé geshitest.php dans le dossier racine du site :

  1. <html>
  2. <head>
  3. <link href="/css/html4strict.css" rel="stylesheet" type="text/css" media="screen" />
  4. <title>Geshi</title>
  5. </head>
  6. <body>
  7. <p>
  8. <?php
  9. define('ROOT_DIR', dirname(__FILE__));
  10.  
  11. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'library');
  12. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'includes');
  13.  
  14. require_once 'prettyfile.php';
  15.  
  16. echo pretty_file('geshitest.php', 'html4strict');
  17. ?>
  18. </p>
  19. </body>
  20. </html>

Si on accède au document avec un navigateur, on n'obtient pas exactement le résultat attendu.

  1. <html>
  2. <head>
  3. <link href="/css/html4strict.css" rel="stylesheet" type="text/css" media="screen" />
  4. <title>Geshi</title>
  5. </head>
  6. <body>
  7. <p>
  8. <?php
  9. define('ROOT_DIR', dirname(__FILE__));
  10.  
  11. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'library');
  12. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'includes');
  13.  
  14. require_once 'prettyfile.php';
  15.  
  16. echo pretty_file('geshitest.php', 'html4strict');
  17. ?>
  18. </p>
  19. </body>
  20. </html>

Remarquez la balise <link> qui tente d'inclure un fichier de feuille de style nommé /css/html4strict.css. Il ne reste plus qu'à construire ce fichier.

Style

Le HTML retourné par pretty_file ne contient pas du tout de CSS. Pour correctement appliquer le style de votre page, demandez à GeSHi de générer la feuille de style et enregistrez-la dans un fichier à part que vous inclurez dans le document. pretty_file peut le faire. Il suffit de décommenter la ligne qui affiche la feuille de style.

  1. //          echo '<pre>' . PHP_EOL .$geshi->get_stylesheet( ). '</pre>' . PHP_EOL;

Rechargez geshitest.php dans votre navigateur. Le source de la feuille de style est affiché au début.

/**
 * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann
 * (http://qbnz.com/highlighter/ and http://geshi.org/)
 */
.html4strict {font-family:"Courier New", Courier, monospace; font-size:10pt;}
.html4strict .de1, .html4strict .de2 {margin:0; padding:0; background:none; vertical-align:top;}
.html4strict .imp {font-weight: bold; color: red;}
.html4strict li, .html4strict .li1 {font-weight: normal; vertical-align:top;}
.html4strict .ln {width:1px;text-align:right;margin:0;padding:0 2px;vertical-align:top;}
.html4strict .kw2 {color: #000000; font-weight: bold;}
.html4strict .kw3 {color: #000066;}
.html4strict .es0 {color: #000099; font-weight: bold;}
.html4strict .br0 {color: #66cc66;}
.html4strict .sy0 {color: #66cc66;}
.html4strict .st0 {color: #ff0000;}
.html4strict .nu0 {color: #cc66cc;}
.html4strict .sc-1 {color: #808080; font-style: italic;}
.html4strict .sc0 {color: #00bbdd;}
.html4strict .sc1 {color: #ddbb00;}
.html4strict .sc2 {color: #009900;}
.html4strict span.xtra { display:block; }

  1. <html>
  2. <head>
  3. <link href="/css/html4strict.css" rel="stylesheet" type="text/css" media="screen" />
  4. <title>Geshi</title>
  5. </head>
  6. <body>
  7. <p>
  8. <?php
  9. define('ROOT_DIR', dirname(__FILE__));
 10.
 11. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'library');
 12. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'includes');
 13.
 14. require_once 'prettyfile.php';
 15.
 16. echo pretty_file('geshiexample.php', 'html4strict');
 17. ?>
 18. </p>
 19. </body>
 20. </html>

La sortie peut différer selon la version de GeSHi qui est installée. N'hésitez pas à changer quelques propriétés comme font-family ou font-size.

Copiez le CSS et collez-le dans un fichier nommé d'après le langage du code source, html4strict.css dans l'exemple. Sauvegardez le fichier dans un dossier css sous la racine de votre site.

  1. /
    1. includes
      1. geshi
      2. geshi.php
    2. library
      1. prettyfile.php
    3. css
      1. html4strict.css

Après que vous avez généré et sauvegardé tous les fichiers CSS pour tous les différents langages que vous publiez, remettez en commentaire la ligne de code dans prettyfile.php qui affiche la feuille de style.

Maintenant, si on recharge le document, le fichier CSS est bien trouvé et le code source a la bonne apparence.

  1. <html>
  2. <head>
  3. <link href="/css/html4strict.css" rel="stylesheet" type="text/css" media="screen" />
  4. <title>Geshi</title>
  5. </head>
  6. <body>
  7. <p>
  8. <?php
  9. define('ROOT_DIR', dirname(__FILE__));
  10.  
  11. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'library');
  12. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'includes');
  13.  
  14. require_once 'prettyfile.php';
  15.  
  16. echo pretty_file('geshitest.php', 'html4strict');
  17. ?>
  18. </p>
  19. </body>
  20. </html>
Application

La fonction d'iZend qui formate les commentaires à l'aide de GeSHi :

  1. require_once 'geshi.php';
  2.  
  3. function bbcode($s) {
  4.     static $bbcode = array(
  5.             '#\[br\]#is'                            => '<br />',
  6. //          '#\[(h[1-6])\](.+?)\[/\1\]#is'          => '<\1>\2</\1>',
  7.             '#\[(b|i|u|s)\](.+?)\[/\1\]#is'         => '<\1>\2</\1>',
  8.             '#\[(p|pre)\](.+?)\[/\1\]#is'           => '<\1>\2</\1>',
  9.             '#\[quote\](.+?)\[/quote\]#is'          => '<blockquote>\1</blockquote>',
  10.             '#\[(url)\=(.+?)\](.*?)\[/\1\]#ise'     => "filter_var('\\2', FILTER_VALIDATE_URL) ? '<a href=\"\\2\" target=\"_blank\">\\3</a>' : '\\0'",
  11.             '#\[(url)](.*?)\[/\1\]#ise'             => "filter_var('\\2', FILTER_VALIDATE_URL) ? '<a href=\"\\2\" target=\"_blank\">\\2</a>' : '\\0'",
  12.             '#\[(e?mail)\=(.+?)\](.*?)\[/\1\]#ise'  => "filter_var('\\2', FILTER_VALIDATE_EMAIL) ? '<a href=\"mailto:\\2\">\\3</a>' : '\\0'",
  13.             '#\[(e?mail)\](.*?)\[/\1\]#ise'         => "filter_var('\\2', FILTER_VALIDATE_EMAIL) ? '<a href=\"mailto:\\2\">\\2</a>' : '\\0'",
  14.             '#\[code\=(.+?)\](.+?)\[/code\]#ise'    => "bbcode_highlite('\\2', '\\1')",
  15.             '#\[code\](.+?)\[/code\]#ise'           => "bbcode_highlite('\\1')",
  16.     );
  17.  
  18.     $s = preg_replace('#\[code([^\]]*?)\](.*?)\[/code\]#ise', "'[code\\1]'.bbcode_protect('\\2').'[/code]'", $s);
  19.  
  20.     $s = htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
  21.  
  22.     return preg_replace(array_keys($bbcode), array_values($bbcode), $s);
  23. }
  24.  
  25. function bbcode_protect($s) {
  26.     return base64_encode(preg_replace('#\\\"#', '"', $s));
  27. }
  28.  
  29. function bbcode_highlite($s, $language=false) {
  30.     $s = trim(base64_decode($s));
  31.  
  32.     if (!$language) {
  33.         return '<code>' . htmlspecialchars($s, ENT_COMPAT, 'UTF-8') . '</code>';
  34.     }
  35.  
  36.     $geshi = new GeSHi($s, $language);
  37.     $geshi->enable_classes(true);
  38.     $geshi->set_header_type(GESHI_HEADER_DIV);
  39.     $geshi->enable_keyword_links(false);
  40.     $geshi->set_tab_width(4);
  41.  
  42.     $output = $geshi->parse_code();
  43.  
  44.     if ($geshi->error()) {
  45.         return false;
  46.     }
  47.  
  48.     head('stylesheet', 'geshi/' . $language, 'screen');
  49.  
  50.     return '<div class="geshi">' . $output . '</div>';
  51. }

Commentaires

Pour ajouter un commentaire, cliquez ici.