transilvlad
transilvlad

Reputation: 14532

Build documentation from PHP code

I heard that the .NET manual was built from code and the most important parts have manually been improved later.

Googleing has not turned up with anything so far. Most of the stuff I found wore related to coding and documenting best practices. But not documenting that can help later build a manual and especially not the solution that would do it. Even if not restricting the search to PHP.

Does anyone know of a solution preferably built in PHP that can built similar docs from PHP code?

Upvotes: 1

Views: 320

Answers (4)

Bruce Wells
Bruce Wells

Reputation: 654

In a similar vein as to what transilvlad posted, I wrote PHPFUI/InstaDoc to dynamically render documentation. It takes about 5 lines of code to add to your project, and then you will have complete and up to date documentation that reflects your code the last time you wrote it to disk.

It also is completely responsive, so it will run great from your phone or tablet. Or even if you are trying to cram your editor, debugger, web page and documentation onto the same monitor. It even displays PHP source code (highlighted in your preferred style of course) and the git history of the file. I have found it incredibly useful in my PHP work to have all this information live and up to date.

Check out the library. Still a bit young, but open source and always open for suggestions.

Upvotes: 0

transilvlad
transilvlad

Reputation: 14532

Since It seemed like fun I built this for the practice and education.

I have discovered that it is actually more handy then everything else as it will allow me to build my docs as I want them.

I also built a HTML generator for it if anybody wants it I will post it.

Please do not be aggressive in your comments.

<?php

/**moDO(Classes)(Modo)(Modo_Parse)

  @(Description)
    Parses Many One standard code comments and produces an array that can be used to build web manuals.


  @(Syntax){quote}
    object `Modo_Parse` ( string ~$contents~ )


  @(Parameters)
  @(Parameters)($contents)(1){info}
    Contents of the code files that contain code comments to be parsed.

  @(Parameters)($contents)(2){note warn}
    Please note very long contents can cause performance issues. 
    It is recommended that you run the parser file by file.


  @(Return)
    Returns an object that contains a variable `parsed` that contains the resulting array.


  @(Examples)
  @(Examples)(1){info}
    `Example 1:` Basic usage example.

  @(Examples)(2){code php}
  $contents = file_get_contents("Modo_Parse.php");
  $parser = new Modo_Parse($contents);
  print_r($parser->parsed);


  @(Changelog){list}
   (1.0) ~Initial release.~

*/

  /**
  * Parses Many One standard code comments and produces an array that can be used to build manuals.
  * @syntax new Modo_Parse($contents);
  * @contents string containing codes with comments to be parsed
  */
  class Modo_Parse {
    /**
    * Takes the parameter and calls the parser function.
    */
    function __construct($contents) {
      // standardise line breaks
      $contents = str_replace(Array("\r\n", "\n\r", "\n"), Array("\n", "\n", "\r\n"), $contents);

      $this->parsed = $this->parser((string)$contents);
    }

    /**
    * The parser function that does all the work.
    */
    private function parser($contents) {
      $return = Array();

      // Identify docs
      preg_match_all('/(\/\*\*moDO(\([a-z \$_-]+\))+\s+)(.*?)(\s*\r\n*\*\/\r\n)/is', $contents, $docs);

      foreach($docs[0] AS $doc) {
        $records = Array();

        // Extract and prepare log header
        $header = explode("\n", trim($doc));
        $header = trim(rtrim(str_replace("/**moDO", "", $header[0])));
        $header = explode("|", str_replace(Array(")(", "(", ")"), Array("|", "", ""), $header));

        // Identify log records
        preg_match_all('/((@\([a-z0-9 \$_-]+\)+(\{[a-z0-9 _-]+\})?))(.*?)+(?=@\([a-z0-9 \$_-]+\)|\*\/)/is', $doc, $log_records);

        foreach($log_records[0] AS $record) {
          // Separate header and text
          preg_match("/(@(\([a-z0-9 \$_-]+\))+(\{[a-z0-9 _-]+\})?)(.*)/is", trim($record), $separated);

          // Extract and prepare record header and style
          $record_head = str_replace(Array("@", ")(", "(", ")", "{", "}"), Array("", "|", "", "", "~", ""), $separated[1]);
          $record_head = explode("~", $record_head);
          if(count($record_head) == 1) {
            $record_head[] = "";
          }
          $record_head = Array(explode("|", $record_head[0]), $record_head[1]);

          // Prepare record text
          if(!strstr($record_head[1], "code")) {
            $separated[4] = trim(implode("\n", array_map('trim', explode("\n", $separated[4]))));
          }
          $separated[4] = preg_replace("/^(\r\n)(.*)/", "$2", rtrim($separated[4]));

          $record_text = preg_replace(Array("/</", "/>/", "/`(.*?)`/", "/~(.*?)~/"), Array("&lt;", "&gt;", "<b>$1</b>", "<i>$1</i>"), $separated[4]);

          // Find and set Links
          preg_match_all("/(\&gt\;)([a-z-.:\/]+)(( )([a-z .,;:-_]+))?(\&lt\;)/i", $record_text, $anchors, PREG_SET_ORDER);
          if(!empty($anchors)) {
            foreach($anchors AS $anchor) {
              if(empty($anchor[5])) {
                $anchor[5] = $anchor[2];
              }
              $record_text = str_replace($anchor[0], '<a href="' . $anchor[2] . '" target="_blank">' . $anchor[5] . '</a>', $record_text);
            }
          }

          // Merge the data together
          $records = $this->array_merge_recursive_distinct($records, $this->array_chain($record_head[0], Array("style" => $record_head[1], "text" => $record_text)));
        }

        $return[] = Array("log" => $header, "data" => $records);
      }

      return $return;
    }

    /**
    * Turns a list into a chain of keys with the value in the last key.
    */
    private function array_chain(Array $keys, $value) {
      $first = array_shift($keys);

      if (count($keys) === 0) {
        $return = Array($first => $value);
      }
      else {
        $return = Array($first => $this->array_chain($keys, $value));
      }

      return $return;
    }

    /**
    * Distinct recursive array merge.
    */
    private function array_merge_recursive_distinct(Array &$array1, Array &$array2) {
      $merged = $array1;

      foreach($array2 as $key => &$value) {
        if(is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
          $merged[$key] = $this->array_merge_recursive_distinct($merged[$key], $value);
        }
        else {
          $merged[$key] = $value;
        }
      }

      return $merged;
    }
  }

Upvotes: 0

Sergi Juanola
Sergi Juanola

Reputation: 6647

Either PhpDocumentor or ApiGen are good to go. However, the PHP.net may be using a special command, made just for them (or also getting extra info from a database, like the changelogs, and definitely the user examples and votes).

Very importantly, bear in mind that there is not a documentation standard, like, and each solution may implement their own, depending on their needs or vision. Luckily, they all try to do the same as Javadoc does.

I've Googled a little, and I don't see a @changelog tag for Javadoc, so maybe the only way is to do your own structure in each docblock and then parse it with some styles after generating the documentation.

Upvotes: 0

ulkas
ulkas

Reputation: 5918

i believe phpdoc is the term you are looking for:

http://en.wikipedia.org/wiki/PHPDoc

i personaly use http://www.phpdoc.org/ implementation

example: you put this before defining a function

 /**
  * This is the short description for a DocBlock.
  *
  * This is the long description for a DocBlock. This text may contain
  * multiple lines and even some _markdown_.
  *
  *
  * @author  Mike van Riel
  *
  * @since 1.0
  *
  * @param int    $example  This is an example function/method parameter description.
  * @param string $example2 This is a second example.
  * @return int
  */
  function docBlock($example, $example2){
      return $example*intval($example2);
  }

Upvotes: 2

Related Questions