Means.Us.Com

Website DIY - tricks and solutions

PHP: highlight_file with line numbers and CSS classes

PHP’s highlight_file provides an easy way to create HTML encoded source listings for your tutorials and articles. However, highlight_file does not provide line numbers; and creates inline_styles instead of using CSS classes. This may affect page load times.

This tutorial shows how to:
  • trick highlight_file into using classes;
  • add line numbers; and
  • make the lines easily identifiable for copying and pasting.

By using classes, and replacing  ’s, in our line numbering script we end up with compact and readable HTML. Compare the HTML for this single line snippet:

002/* tricks highlight_file into using CSS classes and
Fig 1. HTML using inline styles:

<!-- line 2 --><span style="font-weight:normal !important;
border-right:1px solid grey; color:black;text-align:right;
padding:0 0.2em;margin-right:0.5em;">002</span>
<span style="color: brown;font-weight:bold">/*&nbsp;tricks&nbsp;
highlight_file&nbsp;into&nbsp;using&nbsp;CSS&nbsp;classes&nbsp;and<br />


Fig 2. HTML with classes, and &nbsp;'s replaced by space characters:

<!-- line 2 --><span class="linenum">002</span><span class="aw_comment">
/* tricks highlight_file into using CSS classes and<br />

The PHP script


The usual disclaimer: it works for me; but I can't guarantee it will work, or that it is safe for use on your site. You use the example code at your own risk.The script - not line numbered for easy copy and paste (see next, for example with CSS styles for use with script).
Commented demo of script (generated listing includes CSS class styles for line numbering).
Demo displaying listing & its HTML source for copying and pasting snippets or complete code into other pages or "post editors" such as WordPress).

The script can used directly in a page of PHP; or standalone for copying and pasting the HTML for the lines you want to list.

If you extend the script to accept filename and line numbers as variables; make sure you sanitise the input, and restrict the files that can be source listed.


1. Before we start our script, we need to know what highlight_file outputs


Fig 3: a simple 6 line file "displayed" by highlight_file.

<p>Some HTML</p>
<?php
/* A php comment
2nd line of comment */
echo a_function('a_string', $a_var); 
?>

Fig 4: the actual HTML returned by highlight_file.

<code><span style="color: #000000">
&lt;p&gt;Some&nbsp;HTML&lt;/p&gt;<br />
<span style="color: #0000BB">&lt;?php<br />
</span><span style="color: #FF8000">/*&nbsp;A&nbsp;php&nbsp;comment<br />
2nd&nbsp;line&nbsp;of&nbsp;comment&nbsp;*/<br />
</span><span style="color: #007700">echo&nbsp;</span><span style="color: #0000BB">a_function</span><span style="color: #007700">(</span><span style="color: #DD0000">'a_string'</span><span style="color: #007700">,&nbsp;</span><span style="color: #0000BB">$a_var</span><span style="color: #007700">);&nbsp;<br />
</span><span style="color: #0000BB">?&gt;</span></span></code>
  • The syntax highlighter inserts new lines chars after the first span element, and before the closing code tag (but not where they would help readability of the HTML).
  • Syntax coloring is provided by inline styles in span elements.
  • Lines of text from the source file are separated by <br /> tags.
  • There may, or may not be, a closing </span> immediately before or after the <br />.
  • More than one line may be contained within one span e.g. the multi-line comment.
  • Span elements may be nested/embeded within other span elements.
  • each space character is replaced by an "&nbsp;" bulking out and making the raw HTML more difficult to read

1. Highlight_file and adding classes


Simple, and obvious in hindsight; but I've not seen this injection technique used on any other site.

Fig 5:

005ini_set('highlight.default', '"class="aw_default'); ini_set('highlight.keyword','"class="aw_keyword'); 
006ini_set('highlight.string', '"class="aw_string'); ini_set('highlight.html', '"class="aw_htmlsrc');
007ini_set('highlight.comment', '"class="aw_comment');
008$aw_source = highlight_file('YOUR_FILE.EXT', TRUE);

Line 8: Change 'YOUR_FILE.EXT' to the file you wish to parse. Function highlight_file's second parameter is TRUE so the HTML is returned as a string; allowing us to insert line numbers etc before display.

Lines 5-7: These values are used by PHP's syntax highlighter to determine the color values to use in inline styles. We use these parameters with "incorrectly positioned" double quotes to instead "inject" our CSS classes. See highlight_file using CSS classes for a more detailed explanation.


2. Cleaning up the string of HTML


Fig 6. remove stuff that affects our formating:

009$aw_source = str_replace('<code>', '' , $aw_source );
010$aw_source = str_replace(array("\r\n","\r","\n"),'',$aw_source);
011$aw_source = trim($aw_source);
012$aw_source = str_replace('style="color: "', '' , $aw_source );

Line 9: We are going to explode our string into an array of "lines", and don't want the code tag at the beginning of the first line. So we remove it temporarily.
Line 12: We have tricked highlight_file into using classes, but it has still inserted empty inline styles into every span: style="color: " which we now remove.


3. Splitting the HTML string into individual lines


Fig 7:

014$aw_source=str_replace('<br /></span>','</span><br />',$aw_source);
015$aw_lines = explode("<br />", $aw_source);

Each line of source text is separated by a <br /> tag; so we use it to split our string into an array of lines.

However; a <br /> may be immediately be followed by a closing </span> (see Fig 4) which "belongs to the preceding line". Line 14: ensures these closing spans will be placed with their appropriate line array elements.


4. Replacing &nbsp; (optional)


You should already be aware of how HTML renders space characters and &nbsp;'s.

Fig 8:

HTML: "Four    space chars | Four&nbsp;&nbsp;&nbsp;&nbsp;NBSP chars"
Normaly displays as:
"Four space chars | Four     NBSP chars"

In code, each space character may be significant; so highlight_file replaces them by "&nbsp;"'s to ensure all spaces are rendered. However, if your listing HTML is placed in <pre> tags (which preserve space characters) use of &nbsp; is unnecessary.

If you intend to use <pre> elements you can uncomment line 13. It replaces any "&nbsp;" by a " " making the raw HTML easier to read and smaller in size.

013# $aw_source = str_replace("&nbsp;", ' ', $aw_source);

Important: replacement of nbsp;'s must be done after the the string manipulation in Fig 6 (testing identified unwanted effects if done before other string replacements).


5. Inserting Line Numbers, Formatting and echoing our HTML


Fig 10. formatting output:

016echo "<code>";
017$i = 1;
018foreach ($aw_lines as $aw_line){
019 $line_disp = '<span class="linenum">' . sprintf("%03d",$i) .'</span>';
020 $line_disp .= $aw_line;
021 echo $line_disp . "<br />";
022 $aw_html .= '<span style="color:red">&lt;!-- line ' . $i . ' --&gt;</span>' . htmlentities($line_disp) . '&lt;br /&gt;<br />';
023$i++; } 
024echo "<br /><br /><h3>source of listing for copy and paste</h3><pre>&lt;code&gt;<br />" . $aw_html . '</pre>';
025/* if you don't want to display the HTML source for the listing
026then delete lines 22 and 24 */
027?>

See this demo for example of HTML generated.

Line 16: inserts the code tag which we removed from the string in line 9.

display each line of the listing:
Line 19: inserts our line number span element as a zero filled 3 digit number.
Line 20-21: adds the syntax colored content of the line, and displays it.

display the HTML for the listing:
Line 22: builds our string of encoded HTML that will provide cut and pastable HTML of the listing for use in other pages.
Line 24: after all lines have been processed we display the HTML for the listing.


6. Example CSS for the line number, and inheritance


Fig 11: a style sheet extract of some of the classes used by code listings on this page.

.aw_comment {color:brown; font-weight:bold}
.linenum {font-weight:normal !important; border-right:1px solid grey; color:black; text-align: right; padding:0 0.2em; margin-right:0.5em;}

Highlight_file generated spans may be multi-line (see the comment span in fig 12), so some of the line number span elements we added may be embeded within, and inherit styles, from other spans.

Fig 12.

002/* highlight_file with CSS classes and line numbers
003 NOT THOROUGHLY TESTED - USE AT YOUR OWN RISK

<!-- line 2 --><span class="linenum">002</span> <span class="aw_comment">/* highlight_file with CSS classes and line numbers
<!-- line 3 --><span class="linenum">003 </span> NOT THOROUGHLY TESTED - USE AT YOUR OWN RISK</span>

In the case of Fig 12, line number '003' (but not '002') would inherit a font-weight of bold from the outer comment span. To override this, we assign an attribute of font-weight:normal to the linenum class (see Fig 11).

The start of the comment on a previous line, may also mean that when you copy a snippet that starts midway through a comment (e.g. if you start your copy at line 3 in Fig 12) you may be missing the opening comment span tag, which you will have to manually add.

<!-- line 3 --><span class="linenum">003</span><span class="aw_comment"> NOT THOROUGHLY TESTED - USE AT YOUR OWN RISK</span><br />
<!-- line 4 --> ..... 
<!-- line 5 --> .....

Final notes and afterword


Copying snippets: The previous paragraph identifies one occasion when you may have edit your copied snippet. To save time and avoid complexity, I have ignored this and similar scenarios. The edit required is straight forward; and it is unlikely that you will often display a listing that starts midway through a comment.

Other line numbering scripts (without CSS classes) are listed in the comments section of highlight_file at php.net. I only learn and remember enough PHP for the job in hand; and I don't claim this script is the best solution. But it meets my aims:

  • uses CSS classes;
  • can be used within a page of php, or separately to generate HTML with easily identifiable lines for copy and paste.
  • simple and easy to understand for use in a tutorial.

Critiques, improvements, and alternatives to this script are welcome in the comments below.

Use of classes in this script relies on the absence of parameter sanitisation by ini_set. I can't see this changing, but it is possible that a later release of PHP may validate input to ini_set.

Shameless plug: If you find this article interesting then a "+1" at the top of the page would be really appreciated.


Author Andy W+

2 Comments

  1. Great job!!! [thank you]
    – is it possible to change font-size to 12px?
    – any chmod setting to stop highlight_file from opening a particular file [like mysql connecting string…] ?
    Thank you again – alberto

    • AW
      AW

      May 29, 2014 at 8:22 am

      Hi Alberto,
      I’m in a rush – so some off the top of my head answers:

      in the above script the output is surrounded by a <code> block (line 16) so you could add a style for the code tag e.g. “code {font-family: courier, font-size: 12px;}

      The above script uses a hard coded filename (allowing input of filename and security involved would have made the article way to long).
      If you are allowing filename input then you could whitelist and blacklist certain directories/file extensions in the script. I would also locate files of the type you describe in a directory above the website’s domain “root” directory.

      Try googling something like “PHP filename santisation security”.

Leave a Reply

Your comment will appear after its approved; usually within 12 hours but can be up to a week.
Your email address is optional and will not be published.

Copyright © 2012-2017  Means.Us.Com
This site recommends and is hosted by: Kualo Web Hosting.    
Theme: hemingway
 

Blog home  |  ↑ Top of Page ↑