<?php

    
/**
     * Cleans and formates a XHTML document by removing/cleaning unnecessary whitespace
     * (line breaks, tabs, etc.) and indenting all tags correctly.
     * 
     * @author Till Krüss <till at pralinenschachtel dot de>
     * 
     * @param string $string XHTML which should be cleaned and formatted
     * @return string cleaned XHTML string
     */
    
function clean_xhtml($string) {

        
// do not change tags which are matched by this pattern
        
$keep_regexp '~<script[^>]*>.*?<\/script>|<pre[^>]*>.*?<\/pre>|<textarea[^>]*>.*?<\/textarea>~s';

        
// replace \r\n with \n
        
$string preg_replace('~\r\n~ms'"\n"$string);

        
// replace \r with \n
        
$string preg_replace('~\r~ms'"\n"$string);

        
// remove whitespace from the beginnig
        
$string preg_replace('~^\s+~s'''$string);

        
// remove whitespace from the end
        
$string preg_replace('~\s+$~s'''$string);

        
// store all tag which should remain the same
        
preg_match_all($keep_regexp$string$original_tags);

        
// remove whitespace from the beginning of each line
        
$string preg_replace('~^\s+~m'''$string);

        
// remove whitespace from the end of each line
        
$string preg_replace('~\s+$~m'''$string);

        
// removes empty lines
        
$string preg_replace('~\n\s*(?=\n)~ms'''$string);

        
// removes line breaks inside normal text
        
$string preg_replace('~([^>\s])(\s\s+|\n)([^<\s])~m''$1 $3'$string);

        
// correct indention
        
$indent 0;
        
$string explode("\n"$string);
        foreach (
$string as &$line) {
            
$correction intval(substr($line02) == '</'); // correct indention, if line starts with closing tag
            
$line str_repeat("\t"$indent $correction).$line;
            
$indent += substr_count($line'<'); // indent every tag
            
$indent -= substr_count($line'<!'); // subtract doctype declaration
            
$indent -= substr_count($line'<?'); // subtract processing instructions
            
$indent -= substr_count($line'/>'); // subtract self closing tags
            
$indent -= substr_count($line'</') * 2// subtract closing tags
        
}
        
$string implode("\n"$string);

        
// fetch all tag which could been changed
        
preg_match_all($keep_regexp$string$current_tags);

        
// restore all stored tags
        
foreach ($current_tags[0] as $key => $match) {
            
$string str_replace($match$original_tags[0][$key], $string);
        }

        return 
$string;

    }

?>