Are you happy with your logging solution? Would you help us out by taking a 30-second survey? Click here


An API documentation generator

Subscribe to updates I use Sami

Statistics on Sami

Number of watchers on Github 1790
Number of open issues 34
Main language PHP
Average time to merge a PR 15 days
Open pull requests 13+
Closed pull requests 29+
Last commit over 1 year ago
Repo Created over 7 years ago
Repo Last Updated over 1 year ago
Size 732 KB
Organization / Authorfriendsofphp
Page Updated
Do you use Sami? Leave a review!
View open issues (34)
View Sami activity
View on github
Fresh, new opensource launches 🚀🚀🚀
Trendy new open source projects in your inbox! View examples

Subscribe to our mailing list

Evaluating Sami for your project? Score Explanation
Commits Score (?)
Issues & PR Score (?)

Sami: an API documentation generator

Curious about what Sami generates? Have a look at the Symfony API_.


.. caution::

Sami requires **PHP 7**.

Get Sami as a phar file_:

.. code-block:: bash

$ curl -O

Check that everything worked as expected by executing the sami.phar file without any arguments:

.. code-block:: bash

$ php sami.phar

.. note::

Installing Sami as a regular Composer dependency is NOT supported. Sami is
a tool, not a library. As such, it should be installed as a standalone
package, so that Sami's dependencies do not interfere with your project's


Before generating documentation, you must create a configuration file. Here is the simplest possible one:

.. code-block:: php


return new Sami\Sami('/path/to/symfony/src');

The configuration file must return an instance of Sami\Sami and the first argument of the constructor is the path to the code you want to generate documentation for.

Actually, instead of a directory, you can use any valid PHP iterator (and for that matter any instance of the Symfony Finder_ class):

.. code-block:: php


use Sami\Sami;
use Sami\RemoteRepository\GitHubRemoteRepository;
use Symfony\Component\Finder\Finder;

$iterator = Finder::create()

return new Sami($iterator);

The Sami constructor optionally takes an array of options as a second argument:

.. code-block:: php

return new Sami($iterator, array(
    'theme'                => 'symfony',
    'title'                => 'Symfony2 API',
    'build_dir'            => __DIR__.'/build',
    'cache_dir'            => __DIR__.'/cache',
    'remote_repository'    => new GitHubRemoteRepository('username/repository', '/path/to/repository'),
    'default_opened_level' => 2,

And here is how you can configure different versions:

.. code-block:: php


use Sami\Sami;
use Sami\RemoteRepository\GitHubRemoteRepository;
use Sami\Version\GitVersionCollection;
use Symfony\Component\Finder\Finder;

$iterator = Finder::create()
    ->in($dir = '/path/to/symfony/src')

// generate documentation for all v2.0.* tags, the 2.0 branch, and the master one
$versions = GitVersionCollection::create($dir)
    ->add('2.0', '2.0 branch')
    ->add('master', 'master branch')

return new Sami($iterator, array(
    'theme'                => 'symfony',
    'versions'             => $versions,
    'title'                => 'Symfony2 API',
    'build_dir'            => __DIR__.'/../build/sf2/%version%',
    'cache_dir'            => __DIR__.'/../cache/sf2/%version%',
    'remote_repository'    => new GitHubRemoteRepository('symfony/symfony', dirname($dir)),
    'default_opened_level' => 2,

To generate documentation for a PHP 5.2 project, simply set the simulate_namespaces option to true.

You can find more configuration examples under the examples/ directory of the source code.

Sami only documents the public API (public properties and methods); override the default configured filter to change this behavior:

.. code-block:: php


use Sami\Parser\Filter\TrueFilter;

$sami = new Sami(...);
// document all methods and properties
$sami['filter'] = function () {
    return new TrueFilter();


Now that we have a configuration file, let's generate the API documentation:

.. code-block:: bash

$ php sami.phar update /path/to/config.php

The generated documentation can be found under the configured build/ directory (note that the client side search engine does not work on Chrome due to JavaScript execution restriction, unless Chrome is started with the --allow-file-access-from-files option -- it works fine in Firefox).

By default, Sami is configured to run in incremental mode. It means that when running the update command, Sami only re-generates the files that needs to be updated based on what has changed in your code since the last execution.

Sami also detects problems in your phpdoc and can tell you what you need to fix if you add the -v option:

.. code-block:: bash

$ php sami.phar update /path/to/config.php -v

Creating a Theme

If the default themes do not suit your needs, you can very easily create a new one, or just override an existing one.

A theme is just a directory with a manifest.yml file that describes the theme (this is a YAML file):

.. code-block:: yaml

name:   symfony
parent: default

The above configuration creates a new symfony theme based on the default built-in theme. To override a template, just create a file with the same name as the original one. For instance, here is how you can extend the default class template to prefix the class name with Class in the class page title:

.. code-block:: jinja

{# pages/class.twig #}

{% extends 'default/pages/class.twig' %}

{% block title %}Class {{ parent() }}{% endblock %}

If you are familiar with Twig, you will be able to very easily tweak every aspect of the templates as everything has been well isolated in named Twig blocks.

A theme can also add more templates and static files. Here is the manifest for the default theme:

.. code-block:: yaml

name: default

    'css/sami.css': 'css/sami.css'
    'css/bootstrap.min.css': 'css/bootstrap.min.css'
    'css/bootstrap-theme.min.css': 'css/bootstrap-theme.min.css'
    'fonts/glyphicons-halflings-regular.eot': 'fonts/glyphicons-halflings-regular.eot'
    'fonts/glyphicons-halflings-regular.svg': 'fonts/glyphicons-halflings-regular.svg'
    'fonts/glyphicons-halflings-regular.ttf': 'fonts/glyphicons-halflings-regular.ttf'
    'fonts/glyphicons-halflings-regular.woff': 'fonts/glyphicons-halflings-regular.woff'
    'js/bootstrap.min.js': 'js/bootstrap.min.js'
    'js/jquery-1.11.1.min.js': 'js/jquery-1.11.1.min.js'
    'js/handlebars.min.js': 'js/handlebars.min.js'
    'js/typeahead.min.js': 'js/typeahead.min.js'

    'index.twig':      'index.html'
    'doc-index.twig':  'doc-index.html'
    'namespaces.twig': 'namespaces.html'
    'classes.twig':    'classes.html'
    'interfaces.twig': 'interfaces.html'
    'traits.twig':     'traits.html'
    'opensearch.twig': 'opensearch.xml'
    'search.twig':     'search.html'
    'sami.js.twig':    'sami.js'

    'namespace.twig': '%s.html'

    'class.twig': '%s.html'

Files are contained into sections, depending on how Sami needs to treat them:

  • static: Files are copied as is (for assets like images, stylesheets, or JavaScript files);

  • global: Templates that do not depend on the current class context;

  • namespace: Templates that should be generated for every namespace;

  • class: Templates that should be generated for every class.

.. _Symfony API: .. _phar file: .. _Finder:

Search Index

The autocomplete and search functionality of Sami is provided through a
search index that is generated based on the classes, namespaces, interfaces,
and traits of a project. You can customize the search index by overriding the
``search_index_extra`` block of ``sami.js.twig``.

The ``search_index_extra`` allows you to extend the default theme and add more
entries to the index. For example, some projects implement magic methods that
are dynamically generated at runtime. You might wish to document these methods
while generating API documentation and add them to the search index.

Each entry in the search index is a JavaScript object that contains the
following keys:

    The type associated with the entry. Built-in types are "Class",
    "Namespace", "Interface", "Trait". You can add additional types specific
    to an application, and the type information will appear next to the search

    The name of the entry. This is the element in the index that is searchable
    (e.g., class name, namespace name, etc).

    The parent of the element (if any). This can be used to provide context for
    the entry. For example, the fromName of a class would be the namespace of
    the class.

    The link to the parent of the entry (if any). This is used to link a child
    to a parent. For example, this would be a link from a class to the class

    A short text description of the entry.

One such example of when overriding the index is useful could be documenting
dynamically generated API operations of a web service client. Here's a simple
example that adds dynamically generated API operations for a web service client
to the search index:

.. code-block:: jinja

    {% extends "default/sami.js.twig" %}

    {% block search_index_extra %}
        {% for operation in operations -%}
            {"type": "Operation", "link": "{{ operation.path }}", "name": "{{ }}", "doc": "{{ operation.doc }}"},
        {%- endfor %}
    {% endblock %}

This example assumes that the template has a variable ``operations`` available
which contains an array of operations.

.. note::

    Always include a trailing comma for each entry you add to the index. Sami
    will take care of ensuring that trailing commas are handled properly.
Sami open pull requests (View All Pulls)
  • Fix issue #158
  • Defaulting to ./sami.cfg.php if not passed a config
  • Created ExcludePrivateFilter.php
  • Updated to Symfony 3
  • Tree parser sets links for namespaces containing sub namespaces
  • FIX parsing of @property Tags in DocBlocks
  • Use JS to select good entry in version dropdown
  • build cache broke source link between versions
  • Feature/add todo list
  • Add extended support for @see tag
  • allow `true` and `false` type-hint
  • Documentation in own directory
  • Update dependencies to current symfony packages
Sami questions on Stackoverflow (View All Questions)
  • Sami API generator - config not passed to theme
  • Prevent a method from being documented [PHPDOC / SAMI]
  • CultureInfo.DisplayName going Sami on me
  • Silverlight 4 and SAMI Subtitles
Sami list of languages used
Other projects in PHP