bucephalus.org
January 2011

ElephantMark (version 1)

ElephantMark is a simple method to generate HTML documents from PHP files by using the plain Markdown text formatting syntax.

elephantmark.php is the script that converts PHP files into HTML documents. It can be used online on a web server and offline, on the Command Line Interface.

Table of contents:

1. ElephantMark syntax

By modifying the PHP comment delimiters (i.e. // or /* and */), ordinary PHP comments turn into Markdown text parts. These parts are converted into HTML.

ElephantMark comprises just three simple syntax rules:

  1. The Markdown block rule

    A Markdown block has the form

    /***
     ... there is the markdown text part ...
    ***/
    

    It starts after a single line beginning with /*** and ends before a single line that begins with ***/.

    For example, this PHP comment with Markdown code

       /***
       __Markdown__ is a great way to produce HTML, but with less effort. Check out the
       [Wikipedia entry on Markdown](http://en.wikipedia.org/wiki/Markdown).
       ***/
    

    will turn into

       <p>
         <strong>Markdown</strong> is a great way to produce HTML, but with less effort.
         Check out the <a href="http://en.wikipedia.org/wiki/Markdown">Wikipedia entry on Markdown</a>.
       </p>
    

    after conversion.

  2. The Markdown line rule

    A Markdown line has the form

       // // ... this line is markdown ...
    

    Everything after the // // (i.e. two slash, one space, two slash, one space) is considered Markdown and converted accordingly.

    Note, that the Markdown really starts after a space symbol, not directly after the last slash. For example, a line beginning with

       // // # Third chapter ...
    

    will turn into

       <h1>Third chapter ...</h1>
    

    But if it begins as

       // //# Third chapter ...
    

    the whole line will be ignored and not recognized as a Markup line.

  3. The literal block rule

    A literal block has the form

       // // //
       ... php code ...
       // // //
    

    The beginning and end of a literal block is indicated by a line that begins with // // //. Everything between these delimiter lines is considered literal PHP code and will be displayed as such. For example

       // // //
       function triple($n) {
         return 3 * $n;
       }
       // // //
    

    will after the conversion appear as

       <pre><code>function triple($n) {
             return 3 * $n;
       }
       </code></pre>
    

    Note, that everything else in a line that begins with // // // is cut off. This means, we can use additional comments to help structuring literal blocks. We could have written our previous example as

       // // // start of the literal block
       function triple($n) {
         return 3 * $n;
       }
       // // // end of the literal block
    

    and we would still have obtained the same HTML result after the conversion.

You should not try to nest these three rules in any way. For example, don't put a literal block inside a Markdown block, etc.

Everything else, i.e. all code of the PHP file, which is not in a Markdown block, on a Markdown line or inside a literal block, is ignored by ElephantMark and will not appear after the conversion.

Example

A small PHP script with ElephantMark is given by the example.php file with the following content:


   <?php

   // // ## The description

   /***
   This `example.php` script is the PHP version for
   [the standard first program](http://en.wikipedia.org/wiki/Hello_world)
   in computer language tutorials.
   ***/

   // // ## The implementation

   // // //
   echo "Hello world!";
   // // //

   ?>
   

The result after the conversion of example.php will then be a HTML document comprising:

   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
   <html>
   <head>
   </head>
   <body>

   <h2>The description</h2>
   <p>
     This <code>example.php</code> script is the PHP version for
     <a href="http://en.wikipedia.org/wiki/Hello_world">the standard first program</a>
     in computer language tutorials.
   </p>

   <h2>The implementation</h2>
   <pre><code>echo "Hello world!";
   </code></pre>

   </body>
   </html>

Here you can see how this document looks in your browser.

2. elephantmark.php - the document generator

With the PHP script file elephantmark.php we can generate the HTML documentation from a PHP file with ElephantMark comments either offline (using the command line interface or CLI) or online on a web server.

2.1 The required PHP scripts

Next to a working installation of PHP itself, we need two PHP files:

  1. markdown.php

    This is the PHP version of the Markdown-to-HTML converter. It is written by Michel Fortin and available wrapped in a ZIP file:

    http://michelf.com/docs/projets/php/markdown-extra-1.2.4.zip

  2. elephantmark.php

    This is the PHP script that converts ElephantMark enriched PHP source code into a HTML document. It is also wrapped in a ZIP file and free for download:

    http://www.bucephalus.org/ElephantMark/elephantmark-1.zip

2.2 Offline application from the command line

Suppose, we want to generate the HTML documentation from the previous example.php file. Make sure, the three files markdown.php, elephantmark.php and example.php are all placed in the current working directory. Optionally, you can use a CSS stylesheet into the HTML document. In that case, make sure the according file, say mystyle.css is placed into the current directory, too.

The full syntax of the command is

php elephantmark.php PHPFILE TITLE CSSFILE

where the three arguments PHPFILE, TITLE and CSSFILE are optional.

To save the generated document in a separate HTMLFILE, use the universal > to redirect the output

php elephantmark.php PHPFILE TITLE CSSFILE > HTMLFILE

The effect of a call with 0, 1, 2 or 3 arguments is then as follows:

2.3 Online application on a web server

elephantmark.php can also be used on a web server by attaching extra parameters php, title and css as a query to the URL. The general syntax for the online document generation is then

http://www.example.com/mypath/elephantmark.php?php=PHPFILE&title=TITLE&css=CSSFILE

As usual, the query string is attached with a ? and the key=VALUE pairs are separated by &. Note, that each one of the three parameters php=PHPFILE, title=TITLE, css=CSSFILE is optional.

Suppose, you want to generate the documentation for the example.php file. Make sure, the three files markdown.php, elephantmark.php and example.php are then in the same working directory. If you wish to add the stylesheet, place mystyle.css there as well. When you now call

http://www.example.com/mypath/elephantmark.php?php=example.php&title=Hello&css=stylesheet.css

you will see a HTML document, which has the form

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Hello</title>
<style type="text/css">
  ... content of mystyle.css ...
</style>
</head>
<body>
  ... converted content of example.php ...
</body>
</html>

If the TITLE string uses spaces or other characters that are not allowed in URLs, then they should be percent-encoded: a space is then a %20, etc. So, strictly speaking, if I want my HTML document to have the title My first example, I should add title=My%20first%20example. However, I realize, that my SeaMonkey browser doesn't need this URL-encoding. It works fine, when I simply write title=My first example in the URL.

Leaving the whole query and just calling

http://www.example.com/mypath/elephantmark.php

generates the document you are reading just now, i.e. it is just an abbreviation for

http://www.example.com/mypath/elephantmark.php?php=elephantmark.php

Appendix A. The implementation of elephantmark.php

As a user of ElephantMark, you will not need this, although this appendix can serve as a nice example itself. It contains a full listing of all PHP code in this file, all relevant code units are displayed in this documentation by wrapping them in literal blocks, as described earlier. Some functions are not properly implemented, yet, although the whole thing itself normally works fine, at least for me.

A.1 Requirements

The Markup-to-HTML conversion is done with the Markdown() function, which is part of the markdown.php script, written by Michel Fortin. We only need Markdown() in the implementation of the generate() function, below.

include "markdown.php";

A.2 The functions

php2markdown()

This is the main function that turns ElephantMarked PHP code into Markdown text. Here is where the three syntax rules are implemented.

function php2markdown ($phpCode) {
  $phpRows = explode ("\n", $phpCode);
  $markdown = '';
  $mode = 'PHP';  // $mode is one of the following: 'PHP', 'MARKDOWN', 'LITERAL'
  foreach ($phpRows as $phpRow) {
    if ($mode == 'PHP') {
      if (substr ($phpRow, 0, 4) == '/***') {
        $mode = 'MARKDOWN';
      } elseif (substr ($phpRow, 0, 8) == '// // //') {
        $mode = 'LITERAL';
        $markdown .= "\n";
      } elseif ($phpRow == '// //' || $phpRow == '// // ') {
        $markdown .= "\n";
      } elseif (substr ($phpRow, 0, 6) == '// // ') {
        $markdown .= substr ($phpRow, 6) . "\n";
      }
    } elseif ($mode == 'MARKDOWN') {
      if (substr ($phpRow, 0, 4) == '***/') {
        $mode = 'PHP';
      } else {
        $markdown .= $phpRow . "\n";
      }
    } elseif ($mode == 'LITERAL') {
      if (substr ($phpRow, 0, 8) == '// // //') {
        $mode = 'PHP';
      } else {
        $markdown .= '    ' . $phpRow . "\n";
          // four initial spaces in a line means code block in Markdown
      }
    } else {
      trigger_error ("UNDEFINED MODE; SOMETHING WENT WRONG!");
    }
  }
  return $markdown;
}

phpCodeContent()

This function is supposed to extract the code between PHP tags <?php ... ?> and cuts off everything else. For example,

$html = '<h1>Begin</h1> <?php echo "Hi there!"; ?> <h1>End</h1> <?php echo "Bye"; ?>';
echo phpCodeContent($html);

should return the string

'echo "Hi there!";  echo "Bye";'

This is a little complicated to implement properly, because cases where the delimiters appear inside a string etc. have to be considered carefully.

My implementation here is just a dummy one, which works fine for me in normal contexts. Certainly some homework for future improvements.

function phpCodeContent ($code) {
  return $code;
}

htmlDocument()

takes HTML content, a document title and an optional stylesheet and wraps that in a proper HTML document form.

function htmlDocument ($html, $title, $css) {
  // if $css is a nonempty string, it is wrapped in a style tag
  if ($css) {
    $css = "<style type=\"text/css\">\n"
         . "<!--\n"
         . $css
         . "-->\n"
         . "</style>\n";
  }
  // the $title, the $css code and the $html content are wrapped into a HTML document and
  $doc = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
        . "<html>\n"
        . "<head>\n"
        . "<title>" . $title . "</title>\n"
        . $css
        . "</head>\n"
        . "<body>\n"
        . $html
        . "</body>\n"
        . "</html>\n";
  return $doc;
}

generate()

is the main function that integrates all previous steps into a whole.

function generate ($phpFile, $title, $cssFile) {
  // 1. determine $php, the PHP source code
  if ($phpFile) {
    if (! file_exists ($phpFile))
      trigger_error ( "The requested PHP file $phpFile does not exist.\n" );
  } else {
    $phpFile = 'elephantmark.php';
  }
  $php = file_get_contents ($phpFile);
  // 2. determine $css, the CSS source code
  if ($cssFile) {
    if (file_exists ($cssFile)) {
      $css = file_get_contents ($cssFile);
    } else {
      trigger_error ( "The requested CSS file $cssFile does not exist.\n" );
    }
  }
  // 3. generate the documentation
  $php = phpCodeContent ($php);              // extract the PHP blocks
  $markdown = php2markdown ($php);           // generate the Markdown
  $html = Markdown ($markdown);              // convert into HTML with the function provided by Michel Fortin
  $doc = htmlDocument ($html, $title, $css); // wrap the HTML into a HTML document with the optional CSS
  return $doc;
}

scriptIsOnline()

This is just an auxiliary function to determine if the elephantmark.php script was called from the CLI or on the web server. There must be more elegant ways to obtain the same answer, but it seems to do the job.

function scriptIsOnline () {
  if     ($_SERVER['argc'] == 0)                             { return TRUE; }
  elseif ($_SERVER['argc'] == 1 && $_SERVER['QUERY_STRING']) { return TRUE;  }
  else                                                       { return FALSE; }
}

A.3 The main program

if (scriptIsOnline())
  echo generate ($_GET['php'], $_GET['title'], $_GET['css']);
else
  echo generate ($_SERVER['argv'][1], $_SERVER['argv'][2], $_SERVER['argv'][3]);

Appendix B. References