PHPackages                             hexmakina/marker - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. hexmakina/marker

ActivePackage[Templating &amp; Views](/categories/templating)

hexmakina/marker
================

Marker is an HTML generator

0.3.0(2y ago)2218[8 issues](https://github.com/HexMakina/Marker/issues)1mitPHPPHP &gt;=7.4

Since Aug 8Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/HexMakina/Marker)[ Packagist](https://packagist.org/packages/hexmakina/marker)[ RSS](/packages/hexmakina-marker/feed)WikiDiscussions main Synced today

READMEChangelog (8)Dependencies (1)Versions (9)Used By (1)

[![Scrutinizer Code Quality](https://camo.githubusercontent.com/5923a3ed30c934067dab042143d7faa7d00fa492d9d9ec071940b510f6ab0a5f/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f4865784d616b696e612f4d61726b65722f6261646765732f7175616c6974792d73636f72652e706e673f623d6d61696e)](https://scrutinizer-ci.com/g/HexMakina/Marker/?branch=main)[![Maintainability](https://camo.githubusercontent.com/e16657e6074a55ef7a812ff7dd7af364a7b9735e21040002c133d1feebad860d/68747470733a2f2f6170692e636f6465636c696d6174652e636f6d2f76312f6261646765732f62326239353062366139323839396163633837362f6d61696e7461696e6162696c697479)](https://codeclimate.com/github/HexMakina/Marker/maintainability)[![PSR-4 Compliant](https://camo.githubusercontent.com/2f4700086d2e2fe807bd70b7479ef20330e582cd6a594f203de5b49015d15ab8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5053522d342d627269676874677265656e)](https://camo.githubusercontent.com/2f4700086d2e2fe807bd70b7479ef20330e582cd6a594f203de5b49015d15ab8/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5053522d342d627269676874677265656e)[![PSR-12 Compliant](https://camo.githubusercontent.com/f5517c4a8a2438b2152b62762e852385c588ff2c1d8a028ba56d0626e1151e94/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5053522d31322d627269676874677265656e)](https://camo.githubusercontent.com/f5517c4a8a2438b2152b62762e852385c588ff2c1d8a028ba56d0626e1151e94/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5053522d31322d627269676874677265656e)[![PHP 7.4 Required](https://camo.githubusercontent.com/f63521cc590300927c6f861886420f8b50b6dc4ebe47f4f7d2712e2a9b3b1dc6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d372e342d627269676874677265656e)](https://camo.githubusercontent.com/f63521cc590300927c6f861886420f8b50b6dc4ebe47f4f7d2712e2a9b3b1dc6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d372e342d627269676874677265656e)[![Latest Stable Version](https://camo.githubusercontent.com/88c278d77d8b999f5d080ab1802767c8769e775bc39b0da8f23c025cedd47701/687474703a2f2f706f7365722e707567782e6f72672f6865786d616b696e612f6d61726b65722f76)](https://packagist.org/packages/hexmakina/marker)[![License](https://camo.githubusercontent.com/8e2f5b4f01895a084f16fc70f87ebcb00f7ce5f347cf2e09a5c4ba06f1a48ed1/687474703a2f2f706f7365722e707567782e6f72672f6865786d616b696e612f6d61726b65722f6c6963656e7365)](https://packagist.org/packages/hexmakina/marker)

Marker
======

[](#marker)

HTML generation classes for PHP.

Hommage to Christian François Bouche-Villeneuve aka Chris Marker.

Installation
------------

[](#installation)

```
composer require hexmakina/marker
```

All classes live in the `HexMakina\Marker` namespace:

```
use HexMakina\Marker\Element;
use HexMakina\Marker\Marker;
use HexMakina\Marker\Form;
```

Contents
--------

[](#contents)

- [`Element`](#class-element) — the core HTML builder.
- [`Marker`](#class-marker) — ergonomic shortcuts for `` and ``.
- [`Form`](#class-form) — labelled form fields.
- [Accessibility helper](#accessibility-helper-removed) — note on the removed `WCAGElement`.

---

Class Element
=============

[](#class-element)

`Element` builds a single HTML element. Its `__toString()` renders the tag, its attributes, and its inner content. You can construct it directly or use the static shorthand.

```
$element = new Element('section', 'Hello World!', [
    'id'          => 'publication',
    'class'       => 'container',
    'data-toggle' => 'modal',
    'data-target' => '#myModal',
]);

echo $element;
// Hello World!
```

Static shorthand
----------------

[](#static-shorthand)

Any undefined static call is treated as a tag name. The first argument is the inner content, the second the attributes array:

```
echo Element::span('inner text', ['class' => 'd-block']);
// inner text

echo Element::p('lorem ipsum');
// lorem ipsum
```

Escaped text versus trusted HTML
--------------------------------

[](#escaped-text-versus-trusted-html)

**By default, inner content and attribute values are escaped** with `htmlspecialchars(..., ENT_QUOTES)`. This is the safe default: any value coming from user input is neutralised.

```
echo new Element('p', 'alert(1)');
// &lt;script&gt;alert(1)&lt;/script&gt;
```

The escaping behaviour is controlled by the **fourth constructor argument, `$formatter`**:

`$formatter` valueEffect`null` (default) or omittedEscapes with `htmlspecialchars(..., ENT_QUOTES)`.`false`Trusted mode: content and attributes are emitted **verbatim**.a `callable`Custom formatter applied to every value.Use `false` **only** when the content is already safe markup that you produced yourself. This is what lets you nest elements:

```
$abbr = new Element('abbr', 'RTFM', ['title' => 'read the manual']);

// Trusted parent: the child markup survives intact.
echo new Element('p', 'See ' . $abbr, [], false);
// See RTFM
```

> ⚠️ **Nesting pitfall.** If you concatenate a child element into a parent that uses the *default* (escaping) formatter, the child's tags are escaped:
>
> ```
> echo new Element('p', 'See ' . $abbr);
> // See &lt;abbr title=&quot;read the manual&quot;&gt;RTFM&lt;/abbr&gt;
> ```
>
>
>
> To nest safely, pass `false` as the formatter on the **parent** (as above), so the parent trusts the already-escaped child. Do not use `false` on an element whose *own* content is untrusted user data.

Void elements
-------------

[](#void-elements)

Void elements (`area`, `base`, `br`, `col`, `embed`, `hr`, `img`, `input`, `link`, `meta`, `param`, `source`, `track`, `wbr`) are rendered without a closing tag. Pass `null` as the content:

```
echo new Element('img', null, ['src' => 'a.jpg', 'alt' => 'x']);
//

echo new Element('br');
//
```

Any content passed to a void element is ignored — a void tag never has a body.

Boolean attributes
------------------

[](#boolean-attributes)

In the attributes array, an entry with an **integer key** (i.e. a bare value with no `=> `) is rendered as a boolean attribute — just its name, no value. String-keyed entries render as `name="value"`.

```
echo new Element('input', null, [
    'type' => 'checkbox',
    'checked',            // integer key -> boolean attribute
    'disabled',           // integer key -> boolean attribute
    'class' => 'c',       // string key  -> name="value"
]);
//
```

Attribute and tag name validation
---------------------------------

[](#attribute-and-tag-name-validation)

Tag and attribute names must match `^[a-zA-Z][a-zA-Z0-9-]*$`. An invalid name throws `\InvalidArgumentException`:

```
new Element('123');            // throws: Invalid HTML tag name "123"
$el = new Element('div');
$el->{'bad attr'} = 'x';       // throws: Invalid HTML attribute name "bad attr"
```

Attributes can also be read, set, checked, and removed as properties:

```
$el = new Element('div', 'body', ['id' => 'main']);
$el->class = 'container';     // set
echo $el->id;                 // "main"
echo isset($el->id) ? 'y':'n';// "y"
unset($el->class);            // remove
```

---

Class Marker
============

[](#class-marker)

`Marker` extends `Element` with two shortcuts whose argument order reads more naturally than the generic form.

```
//  — src and alt come first; alt is copied to title if not given.
echo Marker::img('a.jpg', 'alt text', ['width' => 100]);
//

//  — href first, then label.
echo Marker::a('/dest', 'Click', ['class' => 'nav']);
// Click
```

Both accept an optional fourth `$formatter` argument with the same meaning as on `Element` (`null` to escape, `false` to trust).

---

Class Form
==========

[](#class-form)

`Form` builds form controls, each optionally paired with a ``. Field **values and attributes are always escaped**; only the assembled label+field markup is treated as trusted so its own tags are not double-escaped.

Inputs
------

[](#inputs)

```
echo Form::input('email', 'a@b.com', ['label' => 'Email']);
// Email
```

The `id` defaults to the field `name`. If there is no name/id, no empty `id`attribute is emitted.

Any input type is available through the static shorthand — the method name becomes the `type`:

```
echo Form::hidden('token', 'abc');   //
echo Form::date('due', '2026-01-01');
echo Form::datetime('when', '...');  // type becomes "datetime-local"
echo Form::password('pw', 'secret'); // value is always blanked for passwords
```

A `type` you pass explicitly is respected — including on disabled fields:

```
echo Form::input('agree', '1', ['type' => 'checkbox', 'disabled']);
//
```

Form labels
-----------

[](#form-labels)

A `label` entry in the attributes array attaches a label. Two layouts:

**Sibling label (default)** — a `` next to the field:

```
echo Form::input('email', 'a@b.com', ['label' => 'Email']);
// Email
```

**Wrapping label** — set `label-wrap` to nest the field inside the ``. This is also used automatically when the field has no `id` to bind a sibling label to:

```
echo Form::input('agree', '1', ['type' => 'checkbox', 'label' => 'I agree', 'label-wrap' => true]);
// I agree
```

The internal keys `label`, `label-wrap`, and `tag` are stripped before rendering and never appear as HTML attributes.

Textarea and select
-------------------

[](#textarea-and-select)

```
echo Form::textarea('bio', 'Hello', ['label' => 'Bio']);
// BioHello

echo Form::select('color', [1 => 'Red', 2 => 'Blue'], 2, ['label' => 'Color']);
// ColorRedBlue
```

The selected option is matched by a strict comparison of the string forms of the selected value and each option key, so a numeric-string selected value still matches an integer array key without loose-typing surprises.

Buttons
-------

[](#buttons)

```
echo Form::submit('save', 'Save');
// Save

echo Form::submit('save', 'Save', ['tag' => 'input']);
//    (rendered as a void element)

echo Form::button('Cancel', ['class' => 'btn']);
// Cancel

echo Form::legend('Account');
// Account
```

---

Accessibility helper (removed)
==============================

[](#accessibility-helper-removed)

Earlier versions shipped a `WCAGElement` class that enforced a handful of accessibility rules (required `alt` on ``/``, `` inside ``, `title` on ``, `href` on ``, `lang` on ``, and so on). **This class has been removed and is no longer part of the package.**

`tests/WCAGElementTest.php` still references the removed class and will fail until it is deleted or the class is deliberately reintroduced. Do not rely on `WCAGElement` — it does not exist in `src/`.

###  Health Score

30

—

LowBetter than 61% of packages

Maintenance41

Moderate activity, may be stable

Popularity14

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~145 days

Recently: every ~137 days

Total

8

Last Release

822d ago

PHP version history (3 changes)0.0.1PHP &gt;=7.4

0.0.2PHP &gt;=7.0

0.1.1PHP &gt;=7.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/20891495?v=4)[HexMakina](/maintainers/HexMakina)[@HexMakina](https://github.com/HexMakina)

---

Top Contributors

[![HexMakina](https://avatars.githubusercontent.com/u/20891495?v=4)](https://github.com/HexMakina "HexMakina (70 commits)")

---

Tags

htmlphpphp7

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/hexmakina-marker/health.svg)

```
[![Health](https://phpackages.com/badges/hexmakina-marker/health.svg)](https://phpackages.com/packages/hexmakina-marker)
```

###  Alternatives

[limenius/react-bundle

Client and Server-side react rendering in a Symfony Bundle

3841.2M](/packages/limenius-react-bundle)[pdewit/nova-external-url

An external URL Laravel Nova field.

30383.2k](/packages/pdewit-nova-external-url)[wbrowar/guide

A CMS Guide for Craft CMS.

6154.7k1](/packages/wbrowar-guide)[localgovdrupal/localgov_base

The base theme for LocalGov Drupal websites.

13126.9k5](/packages/localgovdrupal-localgov-base)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
