UmlWriter Book

Laurent Laville

Revision History
Revision 1.2.02017-02-28LL

Table of Contents

I. Getting Started
1. Download
2. Generating a Diagram
3. Generating a diagram statements from a Terminal
4. Generating a diagram image
5. Summary
6. Next
II. User Guide
7. Installation
7.1. Requirements
7.2. Composer
7.3. PHAR
8. The Command-Line
8.1. Command-Line Options
9. Summary
III. Processor Guide
10. Architecture overview
11. Methods
12. Limitations
IV. Reflector Guide
13. Architecture overview
14. Methods

This complete guide documents UmlWriter 1.2.0, published on 2017-02-28.

This work is licensed under the Attribution-Share Alike 3.0 Unported license.

Part I. Getting Started

Chapter 1. Download

We distribute a PHP Archive (PHAR) that contains all required dependencies of UmlWriter bundled in a single file.

Download the latest version

Make it executable, and put it into your $PATH.

$ chmod +x umlwriter-1.2.0.phar
$ mv umlwriter-1.2.0.phar /usr/local/bin/umlwriter

$ umlwriter --version

You can also immediately use the PHAR after you have downloaded it.

$ wget http://bartlett.laurent-laville.org/get/umlwriter-1.2.0.phar
$ php umlwriter-1.2.0.phar --version

With both methods then you have this output :

umlWriter version 1.2.0

Chapter 2. Generating a Diagram

UML diagrams produced, may be either :

  • restricted to a simple class and its direct dependencies.
  • restricted to a single namespace (with all its objects).
  • non-restricted (all namespaces and their objects).

The following diagram processors (output formats) are available:

  • Graphviz syntax with HTML-like labels
  • PlantUML syntax with default fonts and colors
[Tip]

Detailed descriptions and examples of the diagramming languages are available on the Graphviz and PlantUML websites.

The following reverse-engines are supported:

Chapter 3. Generating a diagram statements from a Terminal

  • In graphviz format with bartlett/php-reflect reverse-engine

    $ umlwriter diagram:render --reflector=reflect --processor=graphviz /path/to/data_source
  • In graphviz format with andrewsville/php-token-reflection reverse-engine

    $ umlwriter diagram:render --reflector=tokenreflection --processor=graphviz /path/to/data_source
  • In plantuml format with default reverse-engine (bartlett/php-reflect)

    $ umlwriter diagram:render --processor=plantuml /path/to/data_source

Chapter 4. Generating a diagram image

[Warning]

You must have installed correctly graphviz or plantuml before to continue.

  • With Graphviz and png output format

    $ dot -Tpng -O /path/to/gv_file

    Where /path/to/gv_file is a file contening Graphviz syntax statements produced by the umlwriter command.

  • With PlantUML and png output format

    $ java -jar plantuml.jar -Tpng /path/to/puml_file -o /path/to/ouput/dir

    Where /path/to/puml_file is a file contening PlantUML syntax statements produced by the umlwriter command.

    And /path/to/output/dir is the directory where the image will be generated.

Chapter 5. Summary

Let’s review what we’ve done :

  • downloaded the latest stable PHAR version.
  • created project using Composer.
  • built your first graphviz and plantuml UML diagram (and png image) from any data source.

Chapter 6. Next

Choose your way depending of your skill level.

Part II. User Guide

[Note]

First visit, you are highly recommended to follow chapters in following order.

  1. Installing all necessary UmlWriter components. See Chapter 7, Installation
  2. Running your first parses with the Command-Line interface. See Chapter 8, The Command-Line

Chapter 7. Installation

UmlWriter may be installed in several ways, choose your favorite.

7.1. Requirements

Before you install PHP UmlWriter, you will need an operating system with PHP 5.4.0 or later installed,

UmlWriter requires the spl extensions. This extension is usually compiled and enabled by default.

7.2. Composer

Put a file named composer.json at the root of your project, with the content below:

{
    "require": {
        "bartlett/umlwriter": "^1.2"
    }
}

And ask Composer to install the dependencies:

$ php composer.phar install

Or just invoke Composer to install the latest version:

$ php composer.phar require bartlett/umlwriter
[Tip]

You can also use Composer to create a new project from an existing UmlWriter package. This is the equivalent of doing a git clone checkout followed by a composer install of the vendors.

$ php composer.phar create-project bartlett/umlwriter /path/to/install ^1.2

Where /path/to/install is your install directory.

7.3. PHAR

The recommended way for newbies, or just to have a look on features of this library, is to download a PHP Archive that contain all required dependencies of PHP UmlWriter bundled in a single file.

$ wget http://bartlett.laurent-laville.org/get/umlwriter-1.2.0.phar
$ chmod +x umlwriter-1.2.0.phar
$ mv umlwriter-1.2.0.phar /usr/local/bin/umlwriter
$ umlwriter

You can also immediately use the PHAR after you have downloaded it.

$ wget http://bartlett.laurent-laville.org/get/umlwriter-1.2.0.phar
$ php umlwriter-1.2.0.phar

Chapter 8. The Command-Line

The command-line interface is the easiest way to try and learn the basic UmlWriter features.

[Note]

For all users.

8.1. Command-Line Options

When you invoke the umlwriter command, you should obtain the following commands and options :

                  ___        __    _ _
  _   _ _ __ ___ | \ \      / / __(_) |_ ___ _ __
 | | | | '_ ` _ \| |\ \ /\ / / '__| | __/ _ \ '__|
 | |_| | | | | | | | \ V  V /| |  | | ||  __/ |
  \__,_|_| |_| |_|_|  \_/\_/ |_|  |_|\__\___|_|

umlWriter version 1.2.0

Usage:
  command [options] [arguments]

Options:
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --manifest            Show which versions of dependencies are bundled.
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  help                      Displays help for a command
  list                      Lists commands
 diagram
  diagram:render            Generate diagram statements of all objects
  diagram:render:class      Generate diagram statements of a single class
  diagram:render:namespace  Generate diagram statements of a single namespace

diagram:render

Generate diagram statements of all objects.

$ umlwriter diagram:render --processor=graphviz src/Bartlett/UmlWriter/Processor
digraph G {
        overlap = false;
        node [fontname="Verdana", fontsize="8", shape="none", margin="0", fillcolor="#FEFECE", style="filled"];
        edge [fontname="Verdana", fontsize="8"];
        subgraph cluster_0 {
                label="Bartlett\\UmlWriter\\Processor";
                "Bartlett\\UmlWriter\\Processor\\AbstractProcessor" [label=<
<table border="0" cellborder="1" cellspacing="0">
<tr><td align="center">&lt;&lt; <font color="black"><i>abstract</i></font> &gt;&gt;<br/><b>AbstractProcessor</b></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left"># reflector</td></tr>
<tr><td align="left"># graphId</td></tr>
<tr><td align="left"># objects</td></tr>
<tr><td align="left"># edges</td></tr>
<tr><td align="left">- spaces</td></tr>
<tr><td align="left">- linebreak</td></tr>
</table></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left">+__construct()</td></tr>
<tr><td align="left">+setGraphId()</td></tr>
<tr><td align="left">+renderClass()</td></tr>
<tr><td align="left">+renderNamespace()</td></tr>
<tr><td align="left">+render()</td></tr>
<tr><td align="left">#renderEdges()</td></tr>
<tr><td align="left">#formatLine()</td></tr>
<tr><td align="left">#writeObjectElement()</td></tr>
<tr><td align="left">#writeObjectInheritance()</td></tr>
<tr><td align="left">#writeObjectInterfaces()</td></tr>
<tr><td align="left">#writeConstantElements()</td></tr>
<tr><td align="left">#writePropertyElements()</td></tr>
<tr><td align="left">#writeMethodElements()</td></tr>
<tr><td align="left">#<i>pushObject</i>()</td></tr>
<tr><td align="left">#<i>pushEdge</i>()</td></tr>
<tr><td align="left">#formatClassStereotype()</td></tr>
<tr><td align="left">-reset()</td></tr>
</table></td></tr>
</table>
>];
                "Bartlett\\UmlWriter\\Processor\\GraphvizProcessor" [label=<
<table border="0" cellborder="1" cellspacing="0">
<tr><td align="center">&lt;&lt; <font color="black"><i>class</i></font> &gt;&gt;<br/><b>GraphvizProcessor</b></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left"># namespaceSeparator</td></tr>
</table></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left">#renderObjects()</td></tr>
<tr><td align="left">#renderEdges()</td></tr>
<tr><td align="left">#writeGraphHeader()</td></tr>
<tr><td align="left">#writeGraphFooter()</td></tr>
<tr><td align="left">#pushObject()</td></tr>
<tr><td align="left">#pushEdge()</td></tr>
<tr><td align="left">-attributes()</td></tr>
</table></td></tr>
</table>
>];
                "Bartlett\\UmlWriter\\Processor\\ProcessorInterface" [label=<
<table border="0" cellborder="1" cellspacing="0">
<tr><td align="center">&lt;&lt; <font color="black"><i>interface</i></font> &gt;&gt;<br/><b>ProcessorInterface</b></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left">+ GLOBAL_NAMESPACE</td></tr>
</table></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left">+renderClass()</td></tr>
<tr><td align="left">+renderNamespace()</td></tr>
<tr><td align="left">+render()</td></tr>
</table></td></tr>
</table>
>];
                "Bartlett\\UmlWriter\\Processor\\PlantUMLProcessor" [label=<
<table border="0" cellborder="1" cellspacing="0">
<tr><td align="center">&lt;&lt; <font color="black"><i>class</i></font> &gt;&gt;<br/><b>PlantUMLProcessor</b></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left"># namespaceSeparator</td></tr>
</table></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left">#renderObjects()</td></tr>
<tr><td align="left">#renderEdges()</td></tr>
<tr><td align="left">#writeGraphHeader()</td></tr>
<tr><td align="left">#writeGraphFooter()</td></tr>
<tr><td align="left">#pushObject()</td></tr>
<tr><td align="left">#pushEdge()</td></tr>
</table></td></tr>
</table>
>];
        }
        "Bartlett\\UmlWriter\\Processor\\GraphvizProcessor" -> "Bartlett\\UmlWriter\\Processor\\AbstractProcessor";
        "Bartlett\\UmlWriter\\Processor\\GraphvizProcessor" -> "Bartlett\\UmlWriter\\Processor\\ProcessorInterface" [arrowhead="empty", style="dashed"];
        "Bartlett\\UmlWriter\\Processor\\PlantUMLProcessor" -> "Bartlett\\UmlWriter\\Processor\\AbstractProcessor";
        "Bartlett\\UmlWriter\\Processor\\PlantUMLProcessor" -> "Bartlett\\UmlWriter\\Processor\\ProcessorInterface" [arrowhead="empty", style="dashed"];
}

That may render something like

render_all.out.png

diagram:render:class

Generate diagram statements of a single class.

$ umlwriter diagram:render:class --processor=graphviz vendor/bartlett/php-reflect/src Bartlett\Reflect
digraph G {
        overlap = false;
        node [fontname="Verdana", fontsize="8", shape="none", margin="0", fillcolor="#FEFECE", style="filled"];
        edge [fontname="Verdana", fontsize="8"];
        subgraph cluster_0 {
                label="Bartlett";
                "Bartlett\\Reflect" [label=<
<table border="0" cellborder="1" cellspacing="0">
<tr><td align="center">&lt;&lt; <font color="black"><i>class</i></font> &gt;&gt;<br/><b>Reflect</b></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left">- analysers</td></tr>
<tr><td align="left">- dataSourceId</td></tr>
</table></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left">+__construct()</td></tr>
<tr><td align="left">+addAnalyser()</td></tr>
<tr><td align="left">+getAnalysers()</td></tr>
<tr><td align="left">+setDataSourceId()</td></tr>
<tr><td align="left">+getDataSourceId()</td></tr>
<tr><td align="left">+parse()</td></tr>
</table></td></tr>
</table>
>];
        }
        subgraph cluster_1 {
                label="Bartlett\\Reflect\\Event";
                "Bartlett\\Reflect\\Event\\AbstractDispatcher" [label=<
<table border="0" cellborder="1" cellspacing="0">
<tr><td align="center">&lt;&lt; <font color="black"><i>class</i></font> &gt;&gt;<br/><b>AbstractDispatcher</b></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left"># eventDispatcher</td></tr>
</table></td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left">+setEventDispatcher()</td></tr>
<tr><td align="left">+getEventDispatcher()</td></tr>
<tr><td align="left">+dispatch()</td></tr>
<tr><td align="left">+addSubscriber()</td></tr>
</table></td></tr>
</table>
>];
                "Bartlett\\Reflect\\Event\\DispatcherInterface" [label=<
<table border="0" cellborder="1" cellspacing="0">
<tr><td align="center">&lt;&lt; <font color="black"><i>interface</i></font> &gt;&gt;<br/><b>DispatcherInterface</b></td></tr>
<tr><td> </td></tr>
<tr><td><table border="0" cellspacing="0" cellpadding="2">
<tr><td align="left">+setEventDispatcher()</td></tr>
<tr><td align="left">+getEventDispatcher()</td></tr>
<tr><td align="left">+dispatch()</td></tr>
<tr><td align="left">+addSubscriber()</td></tr>
</table></td></tr>
</table>
>];
        }
        "Bartlett\\Reflect\\Event\\AbstractDispatcher" -> "Bartlett\\Reflect\\Event\\DispatcherInterface" [arrowhead="empty", style="dashed"];
        "Bartlett\\Reflect" -> "Bartlett\\Reflect\\Event\\AbstractDispatcher";
}

That may render something like

render_class.out.png

diagram:render:namespace

Generate diagram statements of a single namespace.

$ umlwriter diagram:render:namespace --processor=plantuml vendor/bartlett/php-reflect/src Bartlett\Reflect\Plugin\Notifier
@startuml
namespace Bartlett.Reflect.Plugin.Notifier  {
        class GrowlNotifier << class >> {
                -growl
                -format
                --
                +__construct()
                +setMessageFormat()
                +getMessageFormat()
                +notify()
        }
        interface NotifierInterface << interface >> {
                +setMessageFormat()
                +getMessageFormat()
                +notify()
        }
}
Bartlett.Reflect.Plugin.Notifier.GrowlNotifier ..|> Bartlett.Reflect.Plugin.Notifier.NotifierInterface
@enduml

That may render something like

render_namespace.out.png

Chapter 9. Summary

Let’s review what we’ve learned about the command-line interface :

  • It’s a Symfony Console Component that can be extended.
  • We can produced Graphviz or PlantUML code syntax ready for image generation.

Part III. Processor Guide

Chapter 10. Architecture overview

Each processor is a specialized version of a graphical engine. UmlWriter version 1.0.0 is able to render only two diagram syntaxes :

But you are free to make your own. Here is the class diagram of processors architecture built with PlantUML processor

[Tip]

How To Build It. With the CLI mode, use the umlwriter command.

$ umlwriter diagram:render:namespace --processor=plantuml /path/to/umlwriter Bartlett\UmlWriter\Processor

Output return the PlantUML diagram syntax, that you may redirect directly to a file

Then build a png image.

$ java -jar plantuml.jar -Tpng /path/to/puml_file -o /path/to/ouput/dir
processor-guide__1.png

Any new processor must implement the Bartlett\UmlWriter\Processor\ProcessorInterface interface.

And to avoid to implement again and again the same methods, you should used the abstract Bartlett\UmlWriter\Processor\AbstractProcessor class, that will do the job for you.

There are three methods to implement :

  • renderClass() that is in charge of return diagram syntax of a single class and its direct dependencies.
  • renderNamespace() that is in charge of return diagram syntax of a single namespace and all its objects.
  • render() that is in charge of return diagram syntax of all namespaces and objects.

Chapter 11. Methods

There are additional protected methods available in the abstract Bartlett\UmlWriter\Processor\AbstractProcessor class.

  • renderEdges() that is in charge of building all links between objects (class, interface, trait)
  • formatLine() that may format line with a left indentation.
  • writeObjectElement() that render details of each object (class, interface, trait) in a namespace
  • writeObjectInheritance() that render the class or interface parent.
  • writeObjectInterfaces() that render all interfaces implemented by a class
  • `writeConstantElements()`that render all constants of a class
  • writePropertyElements() that render all properties of a class
  • writeMethodElements() that render all methods of any objects (class, interface, trait)

There are two abstract methods you should implement :

  • pushObject() to render an object with all details (attributes, operations)
  • pushEdge() to render links (inheritances, implements) between objects

Other specialized methods that each processor must implement :

  • writeGraphHeader() to render header of the main graph
  • writeGraphFooter() to render footer of the main graph

Chapter 12. Limitations

The PlantUML processor is not yet able to print the PHP namespace \ (backslash), that is replaced by the . (dot)

Part IV. Reflector Guide

Chapter 13. Architecture overview

Each reflector is a specialized version of a compatible reverse-engine. UmlWriter version 1.0.0 is able to use only two engines :

Here is the class diagram of reflectors architecture (built with default PlantUML processor)

reflector-guide__1.png

Any new reflector must implement the Bartlett\UmlWriter\Reflector\ReflectorInterface interface.

Chapter 14. Methods

  • getClass() returns a compatible reflection class of the given object (FQN expected)
  • getClasses() returns all objects from all namespaces
  • getNamespace() returns a list of reflection class, of the given namespace