jbzoo / mermaid-php
Generate diagrams and flowcharts with the help of the mermaid script language
Requires
- php: ^8.3
- ext-json: *
Requires (Dev)
- jbzoo/toolbox-dev: ^8.0
This package is auto-updated.
Last update: 2026-07-23 19:35:05 UTC
README
A powerful PHP library for generating Mermaid diagrams programmatically. Create flowcharts, ER diagrams, class diagrams, and timelines with a fluent, object-oriented API.
Table of Contents
- Installation
- Requirements
- Features
- Quick Start
- Diagram Types
- Output Formats
- Development
- License
- Related Projects
Installation
Install via Composer:
composer require jbzoo/mermaid-php
Requirements
- PHP 8.3 or higher
- ext-json
Features
✅ Multiple Diagram Types: Flowcharts, ER diagrams, class diagrams, sequence diagrams, and timelines ✅ Fluent API: Intuitive method chaining for building complex diagrams ✅ Multiple Output Formats: Mermaid syntax, HTML with embedded viewer, live editor URLs ✅ Theme Support: Default, forest, dark, and neutral themes ✅ Rich Customization: Nodes, links, relationships, styling, and more ✅ Type Safety: Full PHP type hints and strict typing ✅ Zero Dependencies: Only requires PHP and ext-json
Quick Start
<?php use JBZoo\MermaidPHP\Graph; use JBZoo\MermaidPHP\Node; use JBZoo\MermaidPHP\Link; // Create a simple flowchart $graph = new Graph(['title' => 'My Workflow']); $graph ->addNode($start = new Node('start', 'Start', Node::ROUND)) ->addNode($process = new Node('process', 'Process Data', Node::SQUARE)) ->addNode($end = new Node('end', 'End', Node::ROUND)) ->addLink(new Link($start, $process)) ->addLink(new Link($process, $end)); // Output Mermaid syntax echo $graph; // Generate HTML with embedded viewer echo $graph->renderHtml(['theme' => 'dark']); // Get live editor URL for debugging echo $graph->getLiveEditorUrl();
Diagram Types
Flowcharts/Graphs
Create complex flowcharts with nodes, links, and subgraphs:
<?php use JBZoo\MermaidPHP\Graph; use JBZoo\MermaidPHP\Link; use JBZoo\MermaidPHP\Node; use JBZoo\MermaidPHP\Render; $graph = (new Graph(['abc_order' => true])) ->addSubGraph($subGraph1 = new Graph(['title' => 'Main workflow'])) ->addSubGraph($subGraph2 = new Graph(['title' => 'Problematic workflow'])) ->addStyle('linkStyle default interpolate basis'); $subGraph1 ->addNode($nodeE = new Node('E', 'Result two', Node::SQUARE)) ->addNode($nodeB = new Node('B', 'Round edge', Node::ROUND)) ->addNode($nodeA = new Node('A', 'Hard edge', Node::SQUARE)) ->addNode($nodeC = new Node('C', 'Decision', Node::CIRCLE)) ->addNode($nodeD = new Node('D', 'Result one', Node::SQUARE)) ->addLink(new Link($nodeE, $nodeD)) ->addLink(new Link($nodeB, $nodeC)) ->addLink(new Link($nodeC, $nodeD, 'A double quote:"')) ->addLink(new Link($nodeC, $nodeE, 'A dec char:♥')) ->addLink(new Link($nodeA, $nodeB, ' Link text<br>/\\!@#$%^&*()_+><\' " ')); $subGraph2 ->addNode($alone = new Node('alone', 'Alone')) ->addLink(new Link($alone, $nodeC)); echo $graph; // Get result as string (or $graph->__toString(), or (string)$graph) $htmlCode = $graph->renderHtml([ 'debug' => true, 'theme' => Render::THEME_DARK, 'title' => 'Example', 'show-zoom' => false, 'mermaid_url' => 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs', ]); // Get result as HTML code for debugging echo $graph->getLiveEditorUrl(); // Get link to live editor
Result
graph TB;
subgraph "Main workflow"
E["Result two"];
B("Round edge");
A["Hard edge"];
C(("Decision"));
D["Result one"];
E-->D;
B-->C;
C-->|"A double quote:#quot;"|D;
C-->|"A dec char:#hearts;"|E;
A-->|"Link text<br>/\!@#$%^#amp;*()_+><' #quot;"|B;
end
subgraph "Problematic workflow"
alone("Alone");
alone-->C;
end
linkStyle default interpolate basis;
Loading
Clickable nodes
Attach a hyperlink (with an optional tooltip and target) to any node; it renders as a Mermaid click directive:
use JBZoo\MermaidPHP\Graph; use JBZoo\MermaidPHP\Node; $graph = (new Graph()) ->addNode($docs = (new Node('D', 'Docs')) ->setUrl('https://github.com/JBZoo/Mermaid-PHP') ->setTooltip('Open the repository') ->setTarget(Node::TARGET_BLANK)); echo $graph; // graph TB; // D("Docs"); // // click D "https://github.com/JBZoo/Mermaid-PHP" "Open the repository" _blank
setUrl() alone is enough; setTooltip() and setTarget() (Node::TARGET_BLANK, TARGET_SELF,
TARGET_PARENT, TARGET_TOP) are optional.
Per-link styles
Give an individual link its own CSS; it renders as a linkStyle <index> ... directive. The index is resolved
automatically from the link's position in the whole graph (sub-graphs and abc_order included):
use JBZoo\MermaidPHP\Graph; use JBZoo\MermaidPHP\Link; use JBZoo\MermaidPHP\Node; $graph = (new Graph()) ->addNode($a = new Node('A')) ->addNode($b = new Node('B')) ->addLink((new Link($a, $b))->setCss('stroke:blue,stroke-width:4px')); echo $graph; // graph TB; // A("A"); // B("B"); // // A-->B; // // linkStyle 0 stroke:blue,stroke-width:4px;
The same CSS can be passed through the id-based helper: $graph->addLinkByIds('A', 'B', '', Link::ARROW, 'stroke:blue');.
ER Diagrams
Build entity-relationship diagrams for database schemas:
<?php use JBZoo\MermaidPHP\ERDiagram\Entity\Entity; use JBZoo\MermaidPHP\ERDiagram\Entity\EntityProperty; use JBZoo\MermaidPHP\ERDiagram\ERDiagram; use JBZoo\MermaidPHP\ERDiagram\Relation\ManyToMany; use JBZoo\MermaidPHP\ERDiagram\Relation\ManyToOne; use JBZoo\MermaidPHP\ERDiagram\Relation\OneToMany; use JBZoo\MermaidPHP\ERDiagram\Relation\OneToOne; use JBZoo\MermaidPHP\ERDiagram\Relation\Relation; use JBZoo\MermaidPHP\Render; $diagram = (new ERDiagram(['title' => 'Order Example'])); $diagram ->addEntity($customerEntity = new Entity('C', 'Customer', props: [ new EntityProperty('id', 'int', [EntityProperty::PRIMARY_KEY], 'ID of user'), new EntityProperty('cash', 'float'), ])) ->addEntity($orderEntity = new Entity('O', 'Order')) ->addEntity($lineItemEntity = new Entity('LI', 'Line-Item')) ->addEntity($deliveryAddressEntity = new Entity('DA', 'Delivery-Address')) ->addEntity($creditCardEntity = new Entity('CC', 'Credit-Card')) ->addRelation(new OneToMany($customerEntity, $orderEntity, 'places', Relation::ONE_OR_MORE)) ->addRelation(new ManyToOne($lineItemEntity, $orderEntity, 'belongs', Relation::ZERO_OR_MORE)) ->addRelation(new ManyToMany($customerEntity, $deliveryAddressEntity, 'uses', Relation::ONE_OR_MORE)) ->addRelation(new OneToOne($customerEntity, $creditCardEntity, 'has', Relation::ONE_OR_MORE)) ; //header('Content-Type: text/plain'); //echo $diagram; // Get result as string (or $graph->__toString(), or (string)$graph) $htmlCode = $diagram->renderHtml([ 'debug' => true, 'theme' => Render::THEME_DARK, 'title' => 'Example', 'show-zoom' => false, 'mermaid_url' => 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs', ]); // Get result as HTML code for debugging echo $diagram->getLiveEditorUrl(); // Get link to live editor
Result
---
title: Order Example
---
erDiagram
"Customer" ||--|{ "Order" : places
"Line-Item" }o--|| "Order" : belongs
"Customer" }o--|{ "Delivery-Address" : uses
"Customer" ||--|| "Credit-Card" : has
"Customer" {
int id PK "ID of user"
float cash
}
Loading
Timeline Diagrams
Create timeline visualizations for chronological data:
<?php use JBZoo\MermaidPHP\Timeline\Timeline; use JBZoo\MermaidPHP\Timeline\Marker; use JBZoo\MermaidPHP\Timeline\Event; $timeline = (new Timeline(['title' => 'History of Social Media Platform'])) ->addSection( (new Timeline(['title' => 'Subsection 1'])) ->addMarker(new Marker('2002', [ new Event('Linkedin') ])) ) ->addSection( (new Timeline(['title' => 'Subsection 2'])) ->addMarker(new Marker('2004', [ new Event('Facebook'), new Event('Google'), ])) ->addMarker(new Marker('2005', [ new Event('Youtube'), ])) ->addMarker(new Marker('2006', [ new Event('Twitter'), ])) ) ; //header('Content-Type: text/plain'); //echo $diagram; // Get result as string (or $timeline->__toString(), or (string)$timeline) $htmlCode = $timeline->renderHtml([ 'debug' => true, 'theme' => Render::THEME_DARK, 'title' => 'Example', 'show-zoom' => false, 'mermaid_url' => 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs', ]); // Get result as HTML code for debugging echo $diagram->getLiveEditorUrl(); // Get link to live editor
Result
timeline
title History of Social Media Platform
section "Subsection 1"
2002 : Linkedin
section "Subsection 2"
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
Loading
Class Diagrams
Generate UML class diagrams with relationships, namespaces, and cardinality:
<?php use JBZoo\MermaidPHP\ClassDiagram\ClassDiagram; use JBZoo\MermaidPHP\ClassDiagram\Concept\Concept; use JBZoo\MermaidPHP\ClassDiagram\Concept\Attribute; use JBZoo\MermaidPHP\ClassDiagram\Concept\Visibility; use JBZoo\MermaidPHP\ClassDiagram\Concept\Method; use JBZoo\MermaidPHP\ClassDiagram\ConceptNamespace\ConceptNamespace; use JBZoo\MermaidPHP\ClassDiagram\Relationship\Relationship; use JBZoo\MermaidPHP\ClassDiagram\Relationship\RelationType; use JBZoo\MermaidPHP\ClassDiagram\Relationship\Cardinality; use JBZoo\MermaidPHP\ClassDiagram\Relationship\Link; use JBZoo\MermaidPHP\Render; $diagram = (new ClassDiagram()) ->setTitle('Animal example') ->setDirection(\JBZoo\MermaidPHP\Direction::TOP_TO_BOTTOM) ->addClass($animalClass = new Concept( identifier: 'Animal', attributes: [ new Attribute('age', 'int', Visibility::PUBLIC), new Attribute('gender', 'String', Visibility::PUBLIC), ], annotation: 'abstract' )) ->addClass($duckClass = new Concept( identifier: 'Duck', attributes: [ new Attribute('beakColor', 'String', Visibility::PUBLIC), ], methods: [ new Method('swim') ], )) ->addRelationship(new Relationship( classA: $duckClass, classB: $animalClass, relationType: RelationType::REALIZATION )) ; //header('Content-Type: text/plain'); //echo $diagram; // Get result as string (or $diagram->__toString(), or (string) $diagram) $htmlCode = $diagram->renderHtml([ 'debug' => true, 'theme' => Render::THEME_DARK, 'title' => 'Example', 'show-zoom' => false, 'mermaid_url' => 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs', ]); // Get result as HTML code for debugging echo $diagram->getLiveEditorUrl(); // Get link to live editor
Result
---
title: Animal example
---
classDiagram
direction TB
class Animal {
<<abstract>>
+int age
+String gender
}
class Duck {
+String beakColor
swim()
}
Duck ..|> Animal
Loading
Sequence Diagrams
<?php use JBZoo\MermaidPHP\SequenceDiagram\ArrowType; use JBZoo\MermaidPHP\SequenceDiagram\Message; use JBZoo\MermaidPHP\SequenceDiagram\Note; use JBZoo\MermaidPHP\SequenceDiagram\NotePosition; use JBZoo\MermaidPHP\SequenceDiagram\Participant; use JBZoo\MermaidPHP\SequenceDiagram\ParticipantType; use JBZoo\MermaidPHP\SequenceDiagram\SequenceDiagram; use JBZoo\MermaidPHP\SequenceDiagram\Block\Alt; use JBZoo\MermaidPHP\SequenceDiagram\Block\Loop; require_once './vendor/autoload.php'; $alice = new Participant('alice', 'Alice'); $john = new Participant('john', 'John', ParticipantType::ACTOR); $diagram = (new SequenceDiagram(['title' => 'Greeting', 'autonumber' => true])) ->addParticipant($alice) ->addParticipant($john) ->addMessage(new Message($alice, $john, 'Hello John', ArrowType::SOLID_ARROW, activateTarget: true)) ->addMessage(new Message($john, $alice, 'Great!', ArrowType::DOTTED_ARROW, deactivateSource: true)) ->addNote(new Note('a friendly hello', NotePosition::OVER, $alice, $john)) ->addStatement( (new Alt('is well')) ->add(new Message($john, $alice, 'All good')) ->addElse('is sick') ->add(new Message($john, $alice, 'Not so good')), ) ->addStatement( (new Loop('every minute')) ->add(new Message($alice, $john, 'ping')), ); echo $diagram; // Mermaid syntax // echo $diagram->renderHtml(['theme' => 'dark']); // full HTML page
Produces:
---
title: Greeting
---
sequenceDiagram
autonumber
participant alice as Alice
actor john as John
alice->>+john: Hello John
john-->>-alice: Great!
Note over alice,john: a friendly hello
alt is well
john->>alice: All good
else is sick
john->>alice: Not so good
end
loop every minute
alice->>john: ping
end
Loading
Output Formats
All diagram types support multiple output formats:
Mermaid Syntax
// Get raw Mermaid syntax $mermaidCode = (string) $diagram; echo $diagram; // or use __toString()
HTML with Embedded Viewer
// Generate complete HTML page with Mermaid viewer $htmlCode = $diagram->renderHtml([ 'theme' => Render::THEME_DARK, // dark, forest, default, neutral 'title' => 'My Diagram', 'show-zoom' => true, 'debug' => false, 'mermaid_url' => 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs' ]);
Self-Hosting & Offline (GDPR)
By default the generated page imports Mermaid from a public CDN. When the browser opens the page it sends the visitor's IP to that third party, which can be a GDPR concern and also prevents fully offline rendering. (The old builds also loaded jQuery from a CDN — that dependency has been removed entirely, the generated page is now jQuery-free.)
Use the mermaid descriptor to control how Mermaid is loaded. It binds the build flavor to its source,
so there is a single, unambiguous option instead of guessing ESM vs UMD from a URL:
// 1) Default — ES module from a CDN (unchanged; equivalent to the legacy 'mermaid_url' option) $diagram->renderHtml(['mermaid' => [ 'kind' => 'esm-url', 'source' => 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs', ]]); // 2) Self-hosted from your OWN domain — no third-party request, browser-cacheable (best for production) $diagram->renderHtml(['mermaid' => [ 'kind' => 'umd-url', 'source' => '/assets/js/mermaid.min.js', ]]); // 3) Inlined into a single self-contained .html file — zero network requests (best for debug/review/CI artifacts) $diagram->renderHtml(['mermaid' => [ 'kind' => 'umd-inline', 'source' => __DIR__ . '/assets/mermaid.min.js', ]]);
Notes:
umd-urlandumd-inlinerequire the standalone UMD build (mermaid.min.js), which is a single self-contained file. The ESM build (mermaid.esm.min.mjs) is only a loader that fetches per-diagram chunks over the network at render time, so it cannot be self-hosted as one file or inlined.- The library does not vendor Mermaid — download
mermaid.min.jsyourself (npm, or from the CDN) and pointsourceat it. Nothing is fetched over the network by PHP during rendering. - "Offline" caveats: inlining removes Mermaid's own requests, but
fa:Font Awesome icons and custom web fonts are not embedded by Mermaid; provide them locally too if you need a provably request-free page.
Live Editor URL
// Get URL to Mermaid live editor for debugging $url = $diagram->getLiveEditorUrl();
Development
Setup
# Install dependencies make update # Run tests make test # Run all tests and code quality checks make test-all # Run code style checks make codestyle
Testing
- Unit Tests: PHPUnit tests in
tests/directory - Code Coverage: Available via
make report-all - Static Analysis: Psalm integration for type safety
Useful Links
- Mermaid Documentation - Official Mermaid.js documentation
- Mermaid Live Editor - Online editor for testing diagrams
- Mermaid on GitHub - Source code and issues
License
MIT
Related Projects
- CI-Report-Converter - Converting different error reports for deep compatibility with popular CI systems.
- Composer-Diff - See what packages have changed after
composer update. - Composer-Graph - Dependency graph visualization of composer.json based on mermaid-js.
- Utils - Collection of useful PHP functions, mini-classes, and snippets for every day.
- Image - Package provides object-oriented way to manipulate with images as simple as possible.
- Data - Extended implementation of ArrayObject. Use files as config/array.
- Retry - Tiny PHP library providing retry/backoff functionality with multiple backoff strategies and jitter support.
- SimpleTypes - Converting any values and measures - money, weight, exchange rates, length, ...