Forget about AddType application/x-httpd-php-source .phps or highlight_string(). This is way better. Not only because it validates under a strict doctype, but it's semantic fluff and completely stylable with CSS.
Inspired by Tom-Eric.
Note: some tokens from http://php.net/tokens are missing, cause I haven't updated this in a while. But you'll get the idea :)
<?phpclassHighlighter{var$defined_functions;// private $defined_functions;var$defined_constants;// private $defined_constants;var$highlightBrackets;// public $highlightBrackets;var$highlightWhiteSpace;// public $highlightWhiteSpace;functionHighlighter(){$this->defined_functions=get_defined_functions();$this->defined_constants=get_defined_constants();$this->highlightBrackets=true;$this->highlightWhiteSpace=false;}functionHighlightPHP($source){$return='';$tokens=token_get_all($source);foreach($tokensas$code){if(is_array($code)){$name=$code[0];$value=htmlentities($code[1]);switch($name){caseT_OPEN_TAG:caseT_OPEN_TAG_WITH_ECHO:$return.='<pre class="php"><code class="php-tag">'.str_replace('<','<',str_replace('>','>', $value)).'</code>';break;caseT_CLOSE_TAG:$return.='<code class="php-tag">'.str_replace('<','<',str_replace('>','>', $value)).'</code></pre>';break;caseT_STRING:if(in_array($value, $this->defined_functions['internal'])){$return.='<code class="function"><a href="http://www.php.net/'. $value .'" title="PHP: '. $value .' - Manual">'. $value .'</a></code>';}elseif(array_key_exists($value, $this->defined_constants)){$return.='<code class="constant" title="Constant value: '. $this->defined_constants[$value].'">'. $value .'</code>';}else{$return.='<code class="function">'.str_replace('<','<',str_replace('>','>', $value)).'</code>';}break;caseT_NUM_STRING:$return.='<code class="numeric string">'.str_replace('<','<',str_replace('>','>', $value)).'</code>';break;caseT_LINE:caseT_FILE:caseT_FUNC_C:caseT_CLASS_C:$return.='<code class="constant">'. $value .'</code>';break;caseT_INLINE_HTML:$return.='<code class="html">'.str_replace('<','<',str_replace('>','>', $value)).'</code>';break;caseT_LNUMBER:caseT_DNUMBER:$return.='<code class="integer">'. $value .'</code>';break;caseT_CONSTANT_ENCAPSED_STRING:$return.='<code class="encapsed">'.str_replace('<','<',str_replace('>','>', $value)).'</code>';break;caseT_OBJECT_OPERATOR:caseT_PAAMAYIM_NEKUDOTAYIM:$return.='<code class="object operator">'. $value .'</code>';break;caseT_ECHO:caseT_PRINT:caseT_IF:caseT_ELSEIF:caseT_INCLUDE:caseT_INCLUDE_ONCE:caseT_REQUIRE:caseT_REQUIRE_ONCE:caseT_ELSE:caseT_FOR:caseT_SWITCH:caseT_WHILE:caseT_RETURN:caseT_ISSET:caseT_UNSET:caseT_EMPTY:caseT_ARRAY:caseT_DO:caseT_DECLARE:caseT_CONTINUE:caseT_NEW:caseT_CASE:caseT_FOREACH:caseT_CONST:caseT_EXIT:caseT_DEFAULT:caseT_AS:caseT_BREAK:caseT_FUNCTION:caseT_CLASS:caseT_EXTENDS:caseT_VAR:$return.='<code class="construct">'. $value .'</code>';break;caseT_AND_EQUAL:caseT_CONCAT_EQUAL:caseT_DIV_EQUAL:caseT_MINUS_EQUAL:caseT_MOD_EQUAL:caseT_MUL_EQUAL:caseT_OR_EQUAL:caseT_PLUS_EQUAL:caseT_DOUBLE_ARROW:caseT_SL_EQUAL:caseT_INC:caseT_DEC:caseT_SR_EQUAL:caseT_XOR_EQUAL:$return.='<code class="assignment operator">'. $value .'</code>';break;caseT_SL:caseT_SR:$return.='<code class="bitwise operator">'. $value .'</code>';break;caseT_BOOLEAN_AND:caseT_BOOLEAN_OR:caseT_LOGICAL_AND:caseT_LOGICAL_OR:caseT_LOGICAL_XOR:$return.='<code class="logical operator">'. $value .'</code>';break;caseT_IS_EQUAL:caseT_IS_GREATER_OR_EQUAL:caseT_IS_IDENTICAL:caseT_IS_NOT_EQUAL:caseT_IS_NOT_IDENTICAL:caseT_IS_SMALLER_OR_EQUAL:$return.='<code class="comparison operator">'. $value .'</code>';break;caseT_COMMENT:caseT_DOC_COMMENT:$return.='<code class="comment">'.str_replace('<','<',str_replace('>','>', $value)).'</code>';break;caseT_VARIABLE:$return.='<var>'. $value .'</var>';break;caseT_WHITESPACE:/* Giving whitespace a wrapper is handy if you want to turn * this off with css: code.whitespace { white-space: normal; } */if($this->highlightWhiteSpace){$return.='<code class="whitespace">'. $value .'</code>';}else{$return.=$value;}break;default:$return.=htmlspecialchars($value);}}else{if($this->highlightBrackets){if($code=='{'||$code=='}'){$return.='<code class="curly bracket">'. $code .'</code>';}elseif($code=='['||$code==']'){$return.='<code class="square bracket">'. $code .'</code>';}elseif($code=='('||$code==')'){$return.='<code class="parenthese bracket">'. $code .'</code>';}elseif($code==':'){$return.='<code class="colon">'. $code .'</code>';}elseif($code==';'){$return.='<code class="semicolon">'. $code .'</code>';}else{$return.=$this->otherOperators($code);}}else{$return.=$this->otherOperators($code);}}}return$return;}functionotherOperators($code){switch($code){case'>':case'<':$return='<code class="comparison operator">'.htmlspecialchars($code).'</code>';break;case'+':case'-':case'*':case'/':case'%':$return='<code class="arithmetic operator">'.htmlspecialchars($code).'</code>';break;case'=':$return='<code class="assignment operator">'. $code .'</code>';break;case'@':$return='<code class="error-control operator">'. $code .'</code>';break;default:$return=htmlspecialchars($code);}return$return;}}?>
Should be kinda obvious, but here goes for some of the Belgians out there (and of course for posterity) :-).
<?php$parser=newHighlighter();$c=file_get_contents('file.php');echo$parser->HighlightPHP($c);?>
<?php$parser=newHighlighter(); $parser->highlightBrackets=false; $c=file_get_contents('file.php');echo$parser->HighlightPHP($c);?>