Generating PHP Documentation with Sami

Share this article

Documenting your methods, classes, and functions is becoming second nature for everyone, so it makes sense to have a way to generate a separate documentation instead of navigating through the source code. In this article, I’m going to introduce you to Sami, the new API documentation generator.

Image of a large tome

What Is a DocBlock?

A DocBlock is a multi-line comment inserted at the top of an implementation (Class, Interface, Method, Attribute…etc). To clarify this, let’s use some code snippets from Laravel.

abstract class Manager
   * The application instance.
   * @var \Illuminate\Foundation\Application
  protected $app;

   * Create a new manager instance.
   * @param \Illuminate\Foundation\Application $app
   * @return void
  public function __construct($app)
    $this->app = $app;

The DocBlock must start with a /**, end with a */, and every line in between should start with a *.
When defining a class attribute or a method, we write a description, and one or more annotations to define more information about the implementation. In these examples, the @param and @var annotation tags were used. You can visit the documentation for each of these annotations to view a list of annotations phpDocumentor supports.

API Documentation Generators

There are many API documentation generators out there, but one of the best is phpDocumentor. One of the main reasons I like Sami, however, is the ability to use your versioned documentation on Github, and you can also use Twig for generating the templates.

Installing Sami

There are two ways to install Sami. The first one is to download the PHAR archive and run it using php.

php sami.phar

The other way is through Composer. You can run the composer require sami/sami:3.0.* command to add the package to your project.

php vendor/sami/sami/sami.php

Sami command

Cloning Laravel’s Documentation

For our example I will generate documentation for the Laravel Illuminate package.

git clone docs

Now our folder structure looks like the following.


The update command is responsible for refreshing the documentation and it’s used like the following.

php vendor/sami/sami/sami.php update config/config.php

Where config.php is the file that describes your documentation structure and how the output is rendered.


The configuration file must return an instance of Sami\Sami. It accepts a Symfony\Component\Finder\Finder instance and an array of options.

// config/config.php

$dir = __DIR__ . '/../docs';

$iterator = Symfony\Component\Finder\Finder::create()

$options = [
    'theme'                => 'default',
    'title'                => 'Laravel API Documentation',
    'build_dir'            => __DIR__ . '/../build/laravel',
    'cache_dir'            => __DIR__ . '/../cache/laravel',

$sami = new Sami\Sami($iterator, $options);

return $sami;

The $dir variable holds our source file location. The $iterator will get all files and select *.php and exclude the build and test directories inside our source path.
The $options variable is self explanatory. The theme is set to default, but we will talk about making your own theme later on. The build_dir holds our output files, and the cache_dir is used for Twig cache, while title is the title of the generated documentation.

Now, open your command line and run the previous update command.

php vendor/sami/sami/sami.php update config/config.php

Sami update

After the command has executed, you can run the built-in PHP server to see how your documentation works. Run php -S localhost:8000 -t build/, and visit http://localhost:8000/laravel/ to see the result.

Using Git Versioning

I mentioned before that one of the main reasons I like Sami is because of its Git versioning integration. The options['versions'] parameter accepts a Sami\Version\GitVersionCollection which holds your Git repository configuration. As an example, let’s create the documentation for the 5.0 and 4.2 branches.

$dir = __DIR__ . '/../docs/src';

$iterator = Symfony\Component\Finder\Finder::create()

$versions = Sami\Version\GitVersionCollection::create($dir)
    ->add('5.0', 'Master')
    ->add('4.2', '4.2');

$options = [
    'theme'                => 'default',
    'versions'             => $versions,
    'title'                => 'Laravel API Documentation',
    'build_dir'            => __DIR__ . '/../build/laravel/%version%',
    'cache_dir'            => __DIR__ . '/../cache/laravel/%version%',

$sami = new Sami\Sami($iterator, $options);

return $sami;

When using versions, your build_dir and cache_dir must contain the %version% tag. The second parameter for the add method is the label displayed inside a select option. You can also use the addFromTags, setFilter methods to filter versions.

php vendor/sami/sami/sami.php update config/config.php

Now your build directory will contain a folder for each version, and the user will have the ability to choose which one he wants to read.

Creating Themes

Until now, we’ve only used the default theme, but Sami is flexible enough to give us the ability to create our own themes.

The vendor/sami/sami/Sami/Resources/themes folder is where the default theme is. However, you must not place your own themes there. Sami provides a way to add your own themes path to the lookup path.

// config/config.php

$templates = $sami['template_dirs'];
$templates[] = __DIR__ . '/../themes/';

$sami['template_dirs'] = $templates;

Now we can have our themes folder in the root of our application. To create a new theme, you need to create a folder and put a manifest.yml file inside it.

// themes/laravel/manifest.yml

name: laravel
parent: default

Our new theme is called laravel and it will extend the default theme properties. As an example, we will try to add some assets to the page and override the behavior of the template.

Adding Assets

Let’s create a css folder inside our theme folder and create a laravel.css file inside it.

// themes/laravel/css/laravel.css

#api-tree a {
    color: #F4645F;
// themes/laravel/manifest.yml
    'css/laravel.css': 'css/laravel.css'

The static configuration section tells Sami to copy the files as they are inside the specified destination. The build folder will contain the new file inside build/laravel/%version%/css/laravel.css.

// themes/laravel/manifest.yml
    'layout/base.twig': 'layout/base.twig'
// themes/laravel/layout/base.twig

{% extends 'default/layout/base.twig' %}

{% block head %}
    {{ parent()  }}
    <link rel="stylesheet" href="css/laravel.css" />
{% endblock %}

Every time Sami wants to load the base layout, it will use the defined file inside your theme. We extend the default base layout to keep the default look and feel, and we inject our stylesheet link to the head block. Calling The parent() function will cause Twig to keep any existing content in the head block, and output it before our link tag.

// config/config.php

$options = [
    'theme'  => 'laravel',
php vendor/sami/sami/sami.php render config/config.php --force

By default Sami will not reload the documentation if nothing has changed. However, using the --force flag will force it to refresh the files. Visit the documentation page in your browser to see that the color of the left navigation links has changed.

Changing Markup

// themes/laravel/manifest.yml

    'namespaces.twig': 'namespaces.twig'

As the name suggests, the namespaces file defines how our namespace content is rendered.

// themes/laravel/namespaces.twig

{% extends 'default/namespaces.twig' %}
{% from "macros.twig" %}

If you open the default/namespaces.twig page, you’ll see that Sami is using macros to simplify the process of extending templates. We will create our own macros.twig file to override the default markup.
Because Twig doesn’t support inheriting and overriding a function inside a macro, I’m going to copy and paste the default theme macros and start editing them.

// themes/laravel/macros.twig

{% macro render_classes(classes) -%}
    <div class="container-fluid underlined">
        {% for class in classes %}
            <div class="row">
                <div class="col-md-6">
                    {% if class.isInterface %}
                        <span class="label label-primary">I</span>
                    {% else %}
                        <span class="label label-info">C</span>
                    {% endif %}

                    {{ _self.class_link(class, true) }}
                <div class="col-md-6">
                    {{ class.shortdesc|desc(class) }}
        {% endfor %}
{%- endmacro %}

The only thing we’ve changed in this macro is the way we distinguish a class from an interface. Sami used to emphasize the interface, but we are going to insert a colored label before every item depending on its type instead.
We didn’t change much on the page, but you may want to try and use bootstrap styles on your pages, make the menu more responsive, and so on.
Now, after regenerating the documentation you can see that when you list a namespace, the classes and interfaces are preceded by labels.



In this article we introduced a new Symfony tool that can help you manage the documentation of your package. You can also create a unique theme for your package docs. You can find the final result of our example on Github. If you have any questions or comments you can post them below.

Frequently Asked Questions (FAQs) about Generating PHP Documentation with Sami

How do I install Sami for generating PHP documentation?

To install Sami, you need to have Composer installed on your system. Once you have Composer, you can install Sami by running the command composer global require sami/sami. This command will install Sami globally on your system. After the installation, you can verify it by running sami --version in your terminal. If Sami is installed correctly, it will display the version number.

How do I configure Sami for my project?

Sami uses a configuration file written in PHP. This file tells Sami where your source code is and where to generate the documentation. You can create a new configuration file using any text editor and save it with a .php extension. The configuration file should return an instance of Sami\Sami. You can specify the directories to parse, the theme to use, and other options in this file.

How do I generate documentation with Sami?

Once you have configured Sami, you can generate the documentation by running the command sami update /path/to/your/config.php. This command will parse your source code and generate the documentation in the directory specified in your configuration file.

Can I customize the look and feel of the generated documentation?

Yes, Sami allows you to customize the theme of the generated documentation. You can specify the theme in your configuration file. Sami comes with a default theme, but you can also create your own theme or use third-party themes.

How do I update the generated documentation when my source code changes?

You can update the generated documentation by running the sami update /path/to/your/config.php command again. Sami will parse your source code again and update the documentation accordingly. You can automate this process by setting up a cron job or a similar task scheduler.

Can I exclude certain files or directories from the documentation?

Yes, you can exclude certain files or directories from the documentation by specifying them in your configuration file. You can use the exclude option to specify the files or directories to exclude.

Can I include private and protected methods in the documentation?

By default, Sami only includes public methods in the documentation. However, you can include private and protected methods by setting the default_visibility option to private or protected in your configuration file.

Can I generate documentation for a specific version of my source code?

Yes, you can generate documentation for a specific version of your source code by specifying the version in your configuration file. You can use the versions option to specify the version.

How do I link to other classes or methods in the documentation?

You can link to other classes or methods in the documentation by using the @link tag in your PHPDoc comments. Sami will automatically create links to the specified classes or methods.

Can I use Sami with other programming languages?

Sami is specifically designed for generating documentation for PHP code. However, you can use other documentation generators for other programming languages. For example, you can use Javadoc for Java, Doxygen for C++, and Sphinx for Python.

Younes RafieYounes Rafie
View Author

Younes is a freelance web developer, technical writer and a blogger from Morocco. He's worked with JAVA, J2EE, JavaScript, etc., but his language of choice is PHP. You can learn more about him on his website.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week