ForgeMarkDown

A small markdown processor that your app can call to turn Markdown text into HTML. It's a single-purpose converter with a deliberately small feature set — everything it recognizes maps directly to semantic HTML, and nothing more. No framework, no build pipe, no opinions about your markup.

Overview

ForgeMarkDown is exposed to your app as the ForgeMarkDownInterface, resolved through the container — you ask for it and call parse():

use Modules\ForgeMarkDown\Contracts\ForgeMarkDownInterface;

final class SomeController
{
    public function __construct(
        private readonly ForgeMarkDownInterface $markdown,
    ) {}

    public function show(): string
    {
        $html = $this->markdown->parse('# Hello, world');
        // returns "

Hello, world

" return $html; } }

Where you'd reach for it

  • Rendering documentation or help text
  • Composing formatted email or message bodies
  • Turning user-written notes into safe HTML
  • Static site / generated content pipelines

How It Works

parse() runs the text through a single pipeline, in order:

  1. Front matter — a leading --- YAML block is detected and stripped.
  2. Block elements — headings, horizontal rules, blockquotes, lists, tables, and code blocks are turned into their HTML.
  3. Inline elements — emphasis, strong, strikethrough, links, images, and inline code inside the remaining text.

The output is trimmed before it's returned, and anything the processor doesn't recognize passes through untouched — so the rendered HTML holds only the structure you asked for.

Inline Syntax

The inline pass recognizes a focused set of constructs:

Markdown Renders
**bold** / __bold__ <strong>
*em* / _em_ <em>
~~strike~~ <del>
[text](url) <a href>
![alt](src) <img>
`code` <code>

Link and image destinations and inline code are escaped, so a URL or snippet can't smuggle raw markup out of your rendered content.

Block Syntax

Headings

# through ###### produce <h1> to <h6>.

Horizontal rules

Three or more ---, ***, or ___ become <hr>.

Blockquotes

A line starting with > becomes a <blockquote>.

Lists

Lines starting with -, *, or + become a <ul> with <li> items.

Tables

GitHub-style pipe tables (header + separator + rows) render to a <table> with <thead> and <tbody>.

Code blocks

Fenced (```, optionally with a language) and indented blocks become <pre><code>.

## The pitch
> One line of context.
1. first
2. second

| Name  | Role   |
|-------|--------|
| Ada   | Admin  |
| Grace | Editor |

Code Blocks

Fenced code blocks carry an optional language, which is reflected as a class on the <code> element (so your syntax highlighter can hook in):

```php
$greeting = 'hello';
```

Rendering to <pre><code class="language-php"></code></pre>. Indented code — four leading spaces or a tab — is also recognized and wrapped in a <pre><code> pair. Block contents are escaped, keeping rendered examples intact rather than executable.

Front Matter

For whole files, parseFile() handles a leading YAML front-matter block — handy for content documents that carry metadata alongside their body:

use Modules\ForgeMarkDown\Contracts\ForgeMarkDownInterface;

$result = $markdown->parseFile('storage/docs/welcome.md');

$content     = $result['content'];      // rendered HTML
$front_matter = $result['front_matter']; // associative array from YAML

The front-matter array is parsed with the yaml extension when it's available; if the YAML can't be parsed, the method returns that as an error entry rather than throwing. parse() itself strips any front matter without attempting to parse it — it's only parseFile() that yields the metadata as data.

From Your Code

Resolve the interface and use the two methods:

  • parse(string $markdown): string — convert a Markdown string to an HTML string.
  • parseFile(string $path): array — read a file, return ['content' => html, 'front_matter' => array].

Everything is injected, so the converter plugs into controllers, commands, notification bodies, or a static-site generator without ceremony — ask for the interface and it's ready.

What It Isn't

It's worth being honest about the scope, because ForgeMarkDown is deliberately small and not a full spec implementation:

  • Not full CommonMark or GFM — it covers a practical subset, not every corner of the Markdown spec.
  • Single-line blockquotes — a quote is one line; multi-line quote blocks aren't assembled.
  • No ordered lists — list markers produce <ul> regardless of the bullet character.
  • Raw cells — table headers and cells pass through as written, so keep content you trust in tables.

When you need the full Markdown surface area, align a dedicated parser with your content — but for rendering trusted, light Markdown this helper keeps the footprint tiny and predictable.

Installation

php forge.php package:install-module --module=ForgeMarkDown

The capability has no commands, no routes, and no config to set — it registers the ForgeMarkDownInterface binding with the container, and you start parsing. (Optional: the yaml extension adds front-matter data parsing for parseFile().)