Location: PHPKode > scripts > Source code highlighting > source-code-highlighting/class.highlighter.php
<?php
/**
 * @brief class for source-code highlighting
 * It uses the PEAR package <http://pear.php.net/package/Text_Highlighter> Text_Highligher. 
 * It generates valid XHTML-strict code. Currently it can highlight the following languages: 
 * C++, CSS, DIFF, DTD, HTML, Java, JavaScript, MySQL, Perl, PHP, Python, Ruby, SQL, XML.
 * You can highligh files, strings or content. To highlight several code inside a content
 * set a tag \<pre lang="?code language?"\>YOUR CODE\</pre\> around your code. ?code language? 
 * stands for your code-language in which the text should be highlighted.
 * ----------------------------------------------------------------------------
 * Contributor page <http://www.optima-software.de>
 * Released under the GNU General Public License V2
 * ----------------------------------------------------------------------------
 * @author Stephan Spies <info(at)optima-software(dot)de>
 * @date 2006/08/03
 * @version 0.9.0t
**/

define('HL_PEAR_PKG_NOT_FOUND', 'Can not highlight text. Install PEAR package <em>Text_Highlighter</em>!');
define('HL_FILE_NOT_FOUND', 'File <em>%s</em> not found!');
define('HL_FILE_CANNOT_READ', 'Can not read file <em>%s</em>');
define('HL_FILE_TO_LARGE', 'A filesize of <strong>%s</strong> KB is not allowed! Max filesize: %s');
define('HL_STRING_TO_LONG', 'Your code string is too long: Max string length: %s');
define('HL_COPY_INFO', '<!-- Sourcecode highlight for webpages by Stephan Spies <http://optima-software.de> -->');
define('HL_COPY_INFO_END', '<!-- END OF sourcecode highlight -->');

class highlighter {
  
  /** @note public members **/
  public $default_language = 'php';
  public $tabsize = 2; // at the moment, this does nothing to PEAR
  public $max_filesize = 1000; // [KB], set 0 for no limit
  public $max_strlength = 10000; // set 0 for no limit
  
  /** @note private members **/
  private $_errors  = array();
  private $_hl;
  private $_code = '';
  private $_as_table;
  private $_numbers;
  
  /**
   * @brief   class constructor, check for PEAR installation
   * @author  Stephan Spies <info(at)optima-software(dot)de>
   * @date    2006/08/01
  **/
  public function __construct (){
    if (@include_once('Text/Highlighter.php')){
      $this->_hl = new Text_Highlighter;
    }
    if ( !is_object($this->_hl) ) {
      $this->addError(HL_PEAR_PKG_NOT_FOUND);
    }
  }//END function __construct
  
  /**
   * @brief   highlight a given string
   * @param   $text string to highlight
   * @param   $language [optional, default=''] programming language for highlighting, if empty we use $this->default_language
   * @param   $as_table [optional, default=false] output a table, not <\ul\> or \<ol\>, not recomended
   * @param   $numbers [optional, default=true] if numbering is true we use a \<ol\> else a \<ul\>
   * @retval  string  highlighted string, better us output() for getting the results
   * @author  Stephan Spies <info(at)optima-software(dot)de>
   * @date    2006/08/01
  **/
  public function highlight_string ($text, $language='', $as_table = false, $numbers = true){ 
    if ( is_object($this->_hl) ) {  
      empty($language) ? $lng = $this->default_language : $lng = $language;
      $options = array(
        'numbers' => $as_table == true ? HL_NUMBERS_TABLE : ( $numbers == true ? HL_NUMBERS_LI : 0 ),
        'tabsize' => $this->tabsize /** @note at the moment, this does nothing to PEAR **/
      );
      $obj = $this->_hl->factory($lng, $options);
      if ( isset($obj->message) ) {
        /** @note PEAR ERROR **/
        $this->addError($obj->message);
        return false;
      } 
      if ( $this->max_strlength != 0 ){
        if ( (strlen($this->_code) + strlen($text)) > $this->max_strlength){
          $this->addError(sprintf(HL_STRING_TO_LONG, $this->max_strlength));
          return false;
        } 
      }
      $text = stripslashes($text);
      $result  = ''; 
      $result .= '<div class="' . $lng . '">' . "\r\n";
      $result .= $obj->highlight($text) . "\r\n";
      $result .= '</div>' . "\r\n"; 
      $this->_code .= $result;
      return $result;
    }//if ( is_object($this->_hl) )
  }//public function highlight_text
  
  /**
   * @brief   highlight a given file
   * @param   $file file to highlight
   * @param   $language [optional, default=''] programming language for highlighting, if empty we use $this->default_language
   * @param   $as_table [optional, default=false] output a table, not <\ul\> or \<ol\>, not recomended
   * @param   $numbers [optional, default=true] if numbering is true we use a \<ol\> else a \<ul\>
   * @retval  string  highlighted string, better us output() for getting the results
   * @author  Stephan Spies <info(at)optima-software(dot)de>
   * @date    2006/08/01
  **/
  public function highlight_file ($file, $language='', $as_table = false, $numbers = true){ 
    if ( $code = $this->open_file($file) ){
      return $this->highlight_string($code, $language, $as_table, $numbers);
    } else {
      return false;
    }
  }// END public function highlight_file
  
  /**
   * @brief   open file and give back code
   * @param   $file filename to open
   * @retval  string text inside file
   * @author  Stephan Spies <info(at)optima-software(dot)de>
   * @date    2006/08/01
  **/  
  private function open_file ($file){
    if (!file_exists($file) ) {
      $this->addError(sprintf(HL_FILE_NOT_FOUND, $file));
      return false;
    }
    $f = fopen($file, 'r+');
    if (!$f){
      $this->addError(sprintf(HL_FILE_CANNOT_READ, $file));
      return false;         
    }
    if ( $this->max_filesize != 0 ){
      $sizeKB = filesize($file) / 1024;
      if ($sizeKB > $this->max_filesize){
        $this->addError(sprintf(HL_FILE_TO_LARGE, round($sizeKB, 2), $this->max_filesize));
        return false;
      }
    }//if ( $this->max_filesize != 0 )
    return fread( $f, filesize($file) );
  }// END private function open_file
  
  /**
   * @brief   highlight a content, this function highlights all code-snippets between <pre lang="language"\> Tags. "language" means the programming language for the current snippet.
   * @param   $text string to highlight
   * @param   $as_table [optional, default=false] output a table, not <\ul\> or \<ol\>, not recomended
   * @param   $numbers [optional, default=true] if numbering is true we use a \<ol\> else a \<ul\>
   * @retval  string content with highlighted code-snippets, better us output() for getting the results
   * @author  Stephan Spies <info(at)optima-software(dot)de>
   * @date    2006/08/01
  **/
  public function highlight_content ($content, $as_table = false, $numbers = true){ 
    $this->_as_table = $as_table;
    $this->_numbers = $numbers;
    $matches = array();
    $match = "/<pre([^>]*)>(.*?)<\/pre>/is";
    $this->_code = preg_replace_callback($match,
          array('self', 'highlight_string_callback'),
          $content
        );
    return $this->_code;
  }//END public function highlight_content
  
  /**
   * @brief   highlight a content stored in a file, this function highlights all code-snippets between <pre lang="language"\> Tags. "language" means the programming language for the current snippet.
   * @param   $file file to highligh
   * @param   $as_table [optional, default=false] output a table, not <\ul\> or \<ol\>, not recomended
   * @param   $numbers [optional, default=true] if numbering is true we use a \<ol\> else a \<ul\>
   * @retval  string content with highlighted code-snippets, better us output() for getting the results
   * @author  Stephan Spies <info(at)optima-software(dot)de>
   * @date    2006/08/01
  **/  
  public function highlight_content_file ($file, $as_table = false, $numbers = true){ 
    if ( $content = $this->open_file($file) ){
      return $this->highlight_content($content, $as_table, $numbers);
    } else {
      return false;
    }
  }//END public function highlight_content
  
  /**
   * @brief   callback function for preg_replace_callback
   * @param   $match
   * @retval  string  highlighted string
   * @author  Stephan Spies <info(at)optima-software(dot)de>
   * @date    2006/08/01
  **/
  private function highlight_string_callback ($match){
    $attr = stripslashes($match[1]);
    $code = stripslashes($match[2]);
    $re_lang = '/\s+lang\s*=\s*["\']?([^"\']+)["\']?/xi';
    $num = preg_match($re_lang, $attr, $lang);
    
    if ($num) {      
      $code = $this->htmlunspecialchars($code); 
      $res = $this->highlight_string($code, $lang[1], $this->_as_table, $this->_numbers);
      return $res;
    }
  }
  
  /**
   * @brief   add error to error stack
   * @param   $msg
   * @author  Stephan Spies <info(at)optima-software(dot)de>
   * @date    2006/08/01
  **/  
  private function addError($msg) {
    if (!empty($msg)){
      $this->_errors[] = array(
        'uri'     => $_SERVER['REQUEST_URI'],
        'message' => $msg
      );
    }//if if (!empty($msg))    
  }//END private function addError
  
  /**
   * @brief   output the results, use this function to get back highlighted string or content
   * @retval  string highlighted string
   * @author  Stephan Spies <info(at)optima-software(dot)de>
   * @date    2006/08/01
  **/   
  public function output(){
    $_res = HL_COPY_INFO . "\r\n";
    if ( sizeof($this->_errors) > 0 ){
      foreach( $this->_errors as $error ) {
        $_res .= 'CLASS ERROR: ' . $error['message'] . ' REQUEST: ' . $error['uri'] . '<br />'; 
      }//END foreach( $this->_errors as $error   
    } else {
      $_res .= $this->_code;
      $this->_code = '';
      $_res  = eregi_replace('<li>&nbsp;<span class="[A-Za-z0-9\-]+"></span></li>', '', $_res);
      $_res  = eregi_replace('<li>&nbsp;<span class="[A-Za-z0-9\-]+"> </span></li>', '', $_res);
      $_res  = eregi_replace('<span class="[A-Za-z0-9\-]+"> </span>', '&nbsp;', $_res);
      $_res  = eregi_replace('<span class="[A-Za-z0-9\-]+"></span>', '', $_res);
    }    
    $_res .= "\r\n" . HL_COPY_INFO_END . "\r\n";
    return $_res;
  }//END private function output
        
  /**
   * @brief   we will reverse the htmlspecialchars, to convert XML entities back to the right character.
   * @param   $code original code
   * @retval  string
   * @author  Jan Tank <http://zeek.luli.de/>
   * @date    unknown
  **/
  private function htmlunspecialchars($code) {
    $func = create_function('$match',
        '$value = intval($match[1]);'.
        'return ($value < 256) ? chr($value) : $match[1];');
    $tran = get_html_translation_table(HTML_ENTITIES);
    $tran = array_flip($tran);
    $code = strtr($code, $tran);
    $code = preg_replace_callback("/&#([0-9]{1,5});/is", $func, $code);
    return $code;
  }//END private function htmlunspecialchars      
}//END class highlighter
?>
Return current item: Source code highlighting