root/eaccelerator/trunk/PHP_Highlight.php

Revision 265, 16.7 kB (checked in by hans, 2 years ago)

fixed svn properties on trunk (eol-style and keywords)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 <?php
2 /**
3  * Define constants for PHP 4 / PHP 5 compatability
4  *
5  * The source of this file can be found at:
6  * http://cvs.php.net/co.php/pear/PHP_Compat/Compat/Constant/T.php?r=HEAD
7  *
8  * It is part of the PEAR PHP_Compat package:
9  * http://pear.php.net/package/PHP_Compat
10  */
11 if (!defined('T_ML_COMMENT')) {
12    define('T_ML_COMMENT', T_COMMENT);
13 }
14 if (!defined('T_DOC_COMMENT')) {
15     define('T_DOC_COMMENT', T_ML_COMMENT);
16 }
17
18 if (!defined('T_OLD_FUNCTION')) {
19     define('T_OLD_FUNCTION', -1);
20 }
21 if (!defined('T_ABSTRACT')) {
22     define('T_ABSTRACT', -1);
23 }
24 if (!defined('T_CATCH')) {
25     define('T_CATCH', -1);
26 }
27 if (!defined('T_FINAL')) {
28     define('T_FINAL', -1);
29 }
30 if (!defined('T_INSTANCEOF')) {
31     define('T_INSTANCEOF', -1);
32 }
33 if (!defined('T_PRIVATE')) {
34     define('T_PRIVATE', -1);
35 }
36 if (!defined('T_PROTECTED')) {
37     define('T_PROTECTED', -1);
38 }
39 if (!defined('T_PUBLIC')) {
40     define('T_PUBLIC', -1);
41 }
42 if (!defined('T_THROW')) {
43     define('T_THROW', -1);
44 }
45 if (!defined('T_TRY')) {
46     define('T_TRY', -1);
47 }
48 if (!defined('T_CLONE')) {
49     define('T_CLONE', -1);
50 }
51
52
53 /**
54  * Improved PHP syntax highlighting.
55  *
56  * Generates valid XHTML output with function referencing
57  * and line numbering.
58  *
59  * Four output methods provide maximum flexibility.
60  *  * toHTML        => Formatted HTML
61  *  * toHtmlComment => Formatted HTML, specifically for comments on messageboards
62  *  * toList        => Ordered lists
63  *  * toArray       => Associative array
64  *
65  * Highlighting can be inline (with styles), or the same as
66  * highlight_file() where colors are taken from php.ini.
67  *
68  * @author      Aidan Lister <aidan@php.net>
69  * @author      Based on an idea by Matthew Harris <shugotenshi@gmail.com>
70  * @version     1.3.0
71  * @link        http://aidanlister.com/repos/v/PHP_Highlight.php
72  */
73 class PHP_Highlight
74 {
75     /**
76      * Hold highlight colors
77      *
78      * Contains an associative array of token types and colours.
79      * By default, it contains the colours as specified by php.ini
80      *
81      * For example, to change the colour of strings, use something
82      * simular to $h->highlight['string'] = 'blue';
83      *
84      * @var         array
85      * @access      public
86      */
87     var $highlight;
88
89     /**
90      * Things to be replaced for formatting or otherwise reasons
91      *
92      * The first element contains the match array, the second the replace
93      * array.
94      *
95      * @var         array
96      * @access      public
97      */
98     var $replace = array(
99         0 => array("\t", ' '),
100         1 => array('&nbsp;&nbsp;&nbsp;&nbsp;', '&nbsp;'));
101
102     /**
103      * Format of the link to the PHP manual page
104      *
105      * @var         string
106      * @access      public
107      */
108     var $manual = '<a href="http://www.php.net/function.%s">%s</a>';
109
110     /**
111      * Format of the span tag to be wrapped around each token
112      *
113      * @var         string
114      * @access      public
115      */
116     var $span;
117
118     /**
119      * Hold the source
120      *
121      * @var         string
122      * @access      private
123      */
124     var $_source = false;
125
126     /**
127      * Hold plaintext keys
128      *
129      * An array of lines which are plaintext
130      *
131      * @var         array
132      * @access      private
133      */
134     var $_plaintextkeys = array();
135
136
137     /**
138      * Constructor
139      *
140      * Populates highlight array
141      *
142      * @param   bool  $inline     If inline styles rather than colors are to be used
143      * @param   bool  $plaintext  Do not format code outside PHP tags
144      */
145     function PHP_Highlight($inline = false)
146     {
147         // Inline
148         if ($inline === false) {
149             // Default colours from php.ini
150             $this->highlight = array(
151                 'string'    => ini_get('highlight.string'),
152                 'comment'   => ini_get('highlight.comment'),
153                 'keyword'   => ini_get('highlight.keyword'),
154                 'bg'        => ini_get('highlight.bg'),
155                 'default'   => ini_get('highlight.default'),
156                 'html'      => ini_get('highlight.html')
157             );
158             $this->span = '<span style="color: %s;">%s</span>';
159         } else {
160             // Basic styles
161             $this->highlight = array(
162                 'string'    => 'string',
163                 'comment'   => 'comment',
164                 'keyword'   => 'keyword',
165                 'bg'        => 'bg',
166                 'default'   => 'default',
167                 'html'      => 'html'
168             );
169             $this->span = '<span class="%s">%s</span>';
170         }
171     }
172
173
174     /**
175      * Load a file
176      *
177      * @access  public
178      * @param   string      $file       The file to load
179      * @return  bool        Returns TRUE
180      */
181     function loadFile($file)
182     {
183         $this->_source = file_get_contents($file);
184         return true;
185     }
186
187
188     /**
189      * Load a string
190      *
191      * @access  public
192      * @param   string      $string     The string to load
193      * @return  bool        Returns TRUE
194      */
195     function loadString($string)
196     {
197         $this->_source = $string;
198         return true;
199     }
200
201
202     /**
203      * Parse the loaded string into an array
204      * Source is returned with the element key corresponding to the line number
205      *
206      * @access  public
207      * @param   bool      $funcref   Reference functions to the PHP manual
208      * @param   bool      $blocks    Whether to ignore processing plaintext
209      * @return  array     An array of highlighted source code
210      */
211     function toArray($funcref = true, $blocks = false)
212     {
213         // Ensure source has been loaded
214         if ($this->_source == false) {
215             return false;
216         }
217
218         // Init
219         $tokens     = token_get_all($this->_source);
220         $manual     = $this->manual;
221         $span       = $this->span;
222         $i          = 0;
223         $out        = array();
224         $out[$i]    = '';
225
226         // Loop through each token
227         foreach ($tokens as $j => $token) {
228             // Single char
229             if (is_string($token)) {
230                 // Skip token2color check for speed
231                 $out[$i] .= sprintf($span, $this->highlight['keyword'], htmlspecialchars($token));
232
233                 // Heredocs behave strangely
234                 list($tb) = isset($tokens[$j - 1]) ? $tokens[$j - 1] : false;
235                 if ($tb === T_END_HEREDOC) {
236                     $out[++$i] = '';
237                 }
238
239                 continue;
240             }
241
242             // Proper token
243             list ($token, $value) = $token;
244
245             // Make the value safe
246             $value = htmlspecialchars($value);
247             $value = str_replace($this->replace[0], $this->replace[1], $value);
248
249             // Process
250             if ($value === "\n") {
251                 // End this line and start the next
252                 $out[++$i] = '';
253             } else {
254                 // Function linking
255                 if ($funcref === true && $token === T_STRING) {
256                     // Look ahead 1, look ahead 2, and look behind 3
257                     if ((isset($tokens[$j + 1]) && $tokens[$j + 1] === '(' ||
258                         isset($tokens[$j + 2]) && $tokens[$j + 2] === '(') &&
259                         isset($tokens[$j - 3][0]) && $tokens[$j - 3][0] !== T_FUNCTION
260                         && function_exists($value)) {
261
262                         // Insert the manual link
263                         $value = sprintf($manual, $value, $value);
264                     }
265                 }
266
267                 // Explode token block
268                 $lines = explode("\n", $value);             
269                 foreach ($lines as $jj => $line) {
270                     $line = trim($line);
271                     if ($line !== '') {
272                         // This next line is helpful for debugging
273                         //$out[$i] .= token_name($token);
274  
275                         // Check for plaintext
276                         if ($blocks === true && $token === T_INLINE_HTML) {
277                             $this->_plaintextkeys[] = $i;
278                             $out[$i] .= $line;
279                         } else {
280                             $out[$i] .= sprintf($span, $this->_token2color($token), $line);
281                         }
282                     }
283
284
285                     // Start a new line
286                     if (isset($lines[$jj + 1])) {
287                         $out[++$i] = '';
288                     }
289                 }
290             }
291         }
292
293         return $out;
294     }
295
296
297     /**
298      * Convert the source to an ordered list.
299      * Each line is wrapped in <li> tags.
300      *
301      * @access  public
302      * @param   bool      $return    Return rather than print the results
303      * @param   bool      $funcref   Reference functions to the PHP manual
304      * @param   bool      $blocks    Whether to use code blocks around plaintext
305      * @return  string    A HTML ordered list
306      */
307     function toList($return = false, $funcref = true, $blocks = true)
308     {
309         // Ensure source has been loaded
310         if ($this->_source == false) {
311             return false;
312         }
313         
314         // Format list
315         $source = $this->toArray($funcref, $blocks);
316         $out = "<ol>\n";
317         foreach ($source as $i => $line) {
318             $out .= "    <li>";
319
320             // Some extra juggling for lines which are not code
321             if (empty($line)) {
322                 $out .= '&nbsp;';
323             } elseif ($blocks === true && in_array($i, $this->_plaintextkeys)) {
324                 $out .= $line;
325             } else {
326                 $out .= "<code>$line</code>";
327             }
328
329             $out .= "</li>\n";
330         }
331         $out .= "</ol>\n";
332
333         if ($return === true) {
334             return $out;
335         } else {
336             echo $out;
337         }
338     }
339
340
341     /**
342      * Convert the source to formatted HTML.
343      * Each line ends with <br />.
344      *
345      * @access  public
346      * @param   bool      $return       Return rather than print the results
347      * @param   bool      $linenum      Display line numbers
348      * @param   string    $format       Specify format of line numbers displayed
349      * @param   bool      $funcref      Reference functions to the PHP manual
350      * @return  string    A HTML block of code
351      */
352     function toHtml($return = false, $linenum = false, $format = null, $funcref = true)
353     {
354         // Ensure source has been loaded
355         if ($this->_source == false) {
356             return false;
357         }
358         
359         // Line numbering
360         if ($linenum === true && $format === null) {
361             $format = '<span>%02d</span> ';
362         }
363  
364         // Format code
365         $source = $this->toArray($funcref);
366         $out = "<code>\n";
367         foreach ($source as $i => $line) {
368             $out .= '    ';
369     
370             if ($linenum === true) {
371                 $out .= sprintf($format, $i);
372             }
373  
374             $out .= empty($line) ? '&nbsp;' : $line;
375             $out .= "<br />\n";
376         }
377         $out .= "</code>\n";
378  
379         if ($return === true) {
380             return $out;
381         } else {
382             echo $out;
383         }
384     }
385
386
387     /**
388      * Convert the source to formatted HTML blocks.
389      * Each line ends with <br />.
390      *
391      * This method ensures only PHP is between <code> blocks.
392      *
393      * @access  public
394      * @param   bool      $return       Return rather than print the results
395      * @param   bool      $linenum      Display line numbers
396      * @param   string    $format       Specify format of line numbers displayed
397      * @param   bool      $reset        Reset the line numbering each block
398      * @param   bool      $funcref      Reference functions to the PHP manual
399      * @return  string    A HTML block of code
400      */
401     function toHtmlBlocks($return = false, $linenum = false, $format = null, $reset = true, $funcref = true)
402     {
403         // Ensure source has been loaded
404         if ($this->_source == false) {
405             return false;
406         }
407         
408         // Default line numbering
409         if ($linenum === true && $format === null) {
410             $format = '<span>%03d</span> ';
411         }
412
413         // Init
414         $source     = $this->toArray($funcref, true);
415         $out        = '';
416         $wasplain   = true;
417         $k          = 0;
418
419         // Loop through each line and decide which block to use
420         foreach ($source as $i => $line) {
421             // Empty line
422             if (empty($line)) {
423                 if ($wasplain === true) {
424                     $out .= '&nbsp;';
425                 } else {
426                     if (in_array($i+1, $this->_plaintextkeys)) {
427                         $out .= "</code>\n";
428                         
429                         // Reset line numbers
430                         if ($reset === true) {
431                             $k = 0;
432                         }
433                     } else {
434                         $out .= '     ';
435                         // Add line number
436                         if ($linenum === true) {
437                             $out .= sprintf($format, ++$k);
438                         }
439                     }
440                 }
441
442             // Plain text
443             } elseif (in_array($i, $this->_plaintextkeys)) {
444                 if ($wasplain === false) {
445                     $out .= "</code>\n";
446                     
447                     // Reset line numbers
448                     if ($reset === true) {
449                         $k = 0;
450                     }
451                 }
452
453                 $wasplain = true;
454                 $out .= str_replace('&nbsp;', ' ', $line);
455
456             // Code
457             } else {
458                 if ($wasplain === true) {
459                     $out .= "<code>\n";
460                 }
461                 $wasplain = false;
462
463                 $out .= '     ';
464                 // Add line number
465                 if ($linenum === true) {
466                     $out .= sprintf($format, ++$k);
467                 }
468                 $out .= $line;
469             }           
470
471             $out .= "<br />\n";
472         }
473
474         // Add final code tag
475         if ($wasplain === false) {
476             $out .= "</code>\n";
477         }
478
479         // Output method
480         if ($return === true) {
481             return $out;
482         } else {
483             echo $out;
484         }
485     }
486
487
488     /**
489      * Assign a color based on the name of a token
490      *
491      * @access  private
492      * @param   int     $token      The token
493      * @return  string  The color of the token
494      */
495     function _token2color($token)
496     {
497         switch ($token):
498             case T_CONSTANT_ENCAPSED_STRING:
499                 return $this->highlight['string'];
500                 break;
501
502             case T_INLINE_HTML:
503                 return $this->highlight['html'];
504                 break;
505
506             case T_COMMENT:
507             case T_DOC_COMMENT:
508             case T_ML_COMMENT:
509                 return $this->highlight['comment'];
510                 break;
511
512             case T_ABSTRACT:
513             case T_ARRAY:
514             case T_ARRAY_CAST:
515             case T_AS:
516             case T_BOOLEAN_AND:
517             case T_BOOLEAN_OR:
518             case T_BOOL_CAST:
519             case T_BREAK:
520             case T_CASE:
521             case T_CATCH:
522             case T_CLASS:
523             case T_CLONE:
524             case T_CONCAT_EQUAL:
525             case T_CONTINUE:
526             case T_DEFAULT:
527             case T_DOUBLE_ARROW:
528             case T_DOUBLE_CAST:
529             case T_ECHO:
530             case T_ELSE:
531             case T_ELSEIF:
532             case T_EMPTY:
533             case T_ENDDECLARE:
534             case T_ENDFOR:
535             case T_ENDFOREACH:
536             case T_ENDIF:
537             case T_ENDSWITCH:
538             case T_ENDWHILE:
539             case T_END_HEREDOC:
540             case T_EXIT:
541             case T_EXTENDS:
542             case T_FINAL:
543             case T_FOREACH:
544             case T_FUNCTION:
545             case T_GLOBAL:
546             case T_IF:
547             case T_INC:
548             case T_INCLUDE:
549             case T_INCLUDE_ONCE:
550             case T_INSTANCEOF:
551             case T_INT_CAST:
552             case T_ISSET:
553             case T_IS_EQUAL:
554             case T_IS_IDENTICAL:
555             case T_IS_NOT_IDENTICAL:
556             case T_IS_SMALLER_OR_EQUAL:
557             case T_NEW:
558             case T_OBJECT_CAST:
559             case T_OBJECT_OPERATOR:
560             case T_PAAMAYIM_NEKUDOTAYIM:
561             case T_PRIVATE:
562             case T_PROTECTED:
563             case T_PUBLIC:
564             case T_REQUIRE:
565             case T_REQUIRE_ONCE:
566             case T_RETURN:
567             case T_SL:
568             case T_SL_EQUAL:
569             case T_SR:
570             case T_SR_EQUAL:
571             case T_START_HEREDOC:
572             case T_STATIC:
573             case T_STRING_CAST:
574             case T_THROW: