Skip to content
Advertisement

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?

Advertisement

Answer

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("rn", "nr", "n"), Array("n", "n", "rn"), $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*rn**/rn)/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("/^(rn)(.*)/", "$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;
    }
  }
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement