Thomas Kratz


PPI::Transform::Doxygen - PPI::Transform class for generating Doxygen input


  use PPI;
  use PPI::Transform::Doxygen;

  my $transform = PPI::Transform::Doxygen->new();

  # appends Doxygen Docs after __END__ (default when no output is given)

  # prints Doxygen docs for use as a doxygen filter
  $transform->file('' => \*STDOUT);


This module is normally used by the script ppi_transform_doxygen that is part of this distribution and acts as a doxygen input filter (look for INPUT_FILTER in the Doxygen docs).

There is already Doxygen::Filter::Perl doing roughly the same task, but it uses special doxygen comments.

The goal of PPI::Transform::Doxygen is to use only POD documentation with a minimal amount of special syntax, while still producing decent results with doxygen.

As doxygen is not able to parse perl directly, the input filter will convert the source so that it will look like C++.


The only thing really needed, is documenting your methods and functions with a POD tag =head2 that contains a function string with parentheses ( it has to match the regular expression /[\w:]+\(.*\)/) like so:

  =head2 do_things()

  This function does things


  sub do_things {
      print "Hi!\n";

or so:

  =head2 class_method $obj THINGY::new(%args)

  Creates a new THINGY object


  sub new {
      my($class, %args) = @_;
      return bless(\%args, $class);

All other POD documentation (including other =head2 tags) is added as HTML (provided by Pod::POM::View::HTML) into the Doxygen section named Detailed Description. IMHO it looks better when this is at the top of the doxygen docs. Look under "DETAILS ON TOP" on how to do that.


The complete syntax of a =head2 function description is:

=head2 [<category>] [<return_value>] <name>(<parameters>)

category (optional)

The category defines the type of the function definition. The values function and class_method result in the function being tagged as static for Doxygen. Other values will be ignored, which will result interpreting the function as method.

return_value (optional)

Since Doxygen expects C++ input, a return value is mandatory and will default to void. A given string will be passed to Doxygen as is, so be careful with non word characters.


The function name with optional package name e.g. My::Module::test. The module will try to map the function name to the current package when none is given. If your code is correctly parsable with PPI, then this should work.

If the corresponding subroutine is not found it will be tagged as virtual to Doxygen. This is useful for dynamically generated functions (e.g via AUTOLOAD). Yes this has nothing to do with the C++ virtual keyword, but so what? If you want to have the virtual subroutine mapped to the correct namespace you will have to add it to the subs name (e.g. MyClass::mysub() )

Subroutine names with leading underscore will be tagged as private for Doxygen.

If there is no package declaration, the subroutine is created in the main namespace, named <script_or_module_name>_main.


The subroutine's comma separated parameter list. References are given in dereference syntax so %$varname specifies a hash reference. This will be given as type name to Doxygen e.g. subname(hash_ref varname).


If you are using subroutine signatures, they will be parsed for information and you can put the pod after the sub declaration like so:

    sub my_sig_sub ($self, $first = 'default', $second=[], %args) {
    =for method $self

    Sub documentation.


        print join(' ', $first, @$second), "\n";
        return $self;

In that case there is no redundant information you'll have to synchronize on each change. In that case the first parameter behind the =for has to be method, function or class_method. The second parameter specifies the return value. Both parameters must be present because the =for tag requires them. There are no defaults.

A conflicting =head2 declaration for the same subroutine will take precedence.


For having the non subroutine POD documentation at the top of the Doxygen page do the following:

  1. Create a doxygen layout XML file with doxygen -l

  2. Edit the XML file. Move <detaileddescription title=""/> up to the line directly behind <briefdescription visible="yes"/>

  3. Specify the file under LAYOUT_FILE in your Doxyfile.


$obj new(%args)


There are 3 optional arguments for extracting a version number, a revision number and the parent class. Their values have to consist of a regex with one capture group. The key <overwrite> defines the behaviour when there is no output device on calling <file()>. Default behaviour is to append the doxygen docs after an __END__ Token. Setting overwrite to a true value will overwrite the input file.

The defaults are:

    rx_version  => qr/our\s*\$VERSION\s*=\s*["']([\d\.]+)/,
    rx_revision => qr/\$(?:Id|Rev|Revision|LastChangedRevision)\:\s*(\d+)\s*\$/,
    rx_parent   => qr/use\s+(?:base|parent|Mojo::Base)\s+["']?([\w:]+)["']?/,
    overwrite   => 0,

file($in, $out)

Start the transformation reading from $in and saving to $out. $in has to be a filename and $out can be a filename or a filehandle. If $out is not given, behaviour is defined by the parameter overwrite (see new()).

document($ppi_doc, $preserve)

This is normally called by file() (see the docs for PPI::Transform). It will convert a PPI::Document object in place.


Thomas Kratz <>



Copyright 2016-2018 Thomas Kratz.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.