Hi. Have you met KvzHTML? It's a standalone PHP Class for generating HTML.

It's been hiding deep inside the caverns of my secret GitHub repo: kvzlib

  • a collection of code snippets too small or unfinished to deserve their own repository. But I find working with this class so pleasant, I thought I'd share the fun.

KvzHTML (yeah, not the most imaginative name, sorry for that) will generate regular HTML or XML, it just makes the job a bit easier on you because of these features:

  • Automatic Table Of Contents support
  • Very compact syntax
  • Tag nesting & automatic indentation

Without wasting your time going on & on about it, just let me show you some examples, and you be the judge.

Introduction

A basic usage example.

<?php
error_reporting(E_ALL);
require_once 'KvzHTML.php';

// These are the default options, so might
// as well have initialized KvzHTML with an
// empty first argument
$H = new KvzHTML(array(
  'xhtml' => true,
  'track_toc' => false,
  'link_toc' => true,
  'indentation' => 4,
  'newlines' => true,
  'echo' => false,
  'buffer' => false,
  'xml' => false,
  'tidy' => false,
));

echo $H->html(
  $H->head(
      $H->title('My page')
  ) .
  $H->body(
      $H->h1('Important website') .
      $H->p('Welcome to our website.') .
      $H->h2('Users') .
      $H->p('Here\'s a list of current users:') .
      $H->table(
          $H->tr($H->th('id') . $H->th('name') . $H->th('age')) .
          $H->tr($H->td('#1') . $H->td('Kevin van Zonneveld') . $H->td('26')) .
          $H->tr($H->td('#2') . $H->td('Foo Bar') . $H->td('28'))
      )
  )
);
?>

Result

<html>
    <head>
        <title>
            My page
        </title>
    </head>
    <body>
        <h1>
            Important website
        </h1>
        <p>
            Welcome to our website.
        </p>
        <h2>
            Users
        </h2>
        <p>
            Here's a list of current users:
        </p>
        <table>
            <tr>
                <th>
                    id
                </th>
                <th>
                    name
                </th>
                <th>
                    age
                </th>
            </tr>
            <tr>
                <td>
                    #1
                </td>
                <td>
                    Kevin van Zonneveld
                </td>
                <td>
                    26
                </td>
            </tr>
            <tr>
                <td>
                    #2
                </td>
                <td>
                    Foo Bar
                </td>
                <td>
                    28
                </td>
            </tr>
        </table>
    </body>
</html>

Automatic TOC

Keeping track of a Table of Contents can become quite tedious because every change you make; you have to make twice. So why not let KvzHTML handle this for you?

In this example I also show you that

  • you don't have to nest KvzHTML functions to nest HTML Elements, per se.

You can open a tag by setting it's body to TRUE, or by leaving it empty.

You can then later close it with ->tag(false)

  • You can set echo on if you don't nest KvzHTML functions, so that every function will immediately get echoed.
  • ..unless you turn on buffering.

all these different options can lead to the same thing. But are there to reduce your need to type to an absolute minimum.

<?php
error_reporting(E_ALL);
require_once 'KvzHTML.php';

// Some options:
// - create a ToC
// - don't automatically create links for ToC navigation
// - echo output, don't return
// - save all echoed output in a buffer
// - Don't automatically Tidy the output (btw, only works with buffer on)
$E = new KvzHTML(array(
  'track_toc' => true,
  'link_toc' => false,
  'echo' => true,
  'buffer' => true,
  'tidy' => false,
));

$E->h1('New application');
$E->p($E->loremIpsum);

$E->h2('Users');
$E->blockquote($E->loremIpsum);

$E->h3('Permissions');
$E->p($E->loremIpsum);

$E->h4('General Concept');
$E->p($E->loremIpsum);

$E->h4('Exceptions');
$E->p($E->loremIpsum);

$E->h3('Usability');
$E->ul(); // An empty body will just open the tag: <ul>
  $E->li('Point 1');
  $E->li('Point 2');
  $E->li();
      $E->strong('Point 3');
      $E->br(null);  // NULL will make a self closing tag: <br />
      $E->span('Has some implications.');
  $E->li(false);
$E->ul(false);  // False will close the tag: </ul>

// Save both chucks so further KvzHTML calls
// wont impact them anymore
$toc    = $E->getToc();
$document = $E->getBuffer();

// Print a heading that says TOC
$E->h1('Table of Contents', array('__buffer' => false));

// Print toc
echo $toc;

// Print original document
echo $document;
?>

Result

<h1>
    Table of Contents
</h1>
<a name='toc_root'></a>
<ul>
 <li>New application</li>
 <ul>
  <li>Users</li>
  <ul>
   <li>Permissions</li>
   <ul>
    <li>General Concept</li>
    <li>Exceptions</li>
    </ul>
   </ul>
   <li>Usability</li>
   </ul>
  </ul>
 </ul>
</ul>
<h1>
    New application
</h1>
<p>
    Lorem ipsum dolor sit amet, consectetur adipisicing
    elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
    ut aliquip ex ea commodo consequat. Duis aute irure dolor in
    reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa
    qui officia deserunt mollit anim id est laborum
</p>
<h2>
    Users
</h2>
<blockquote>
    Lorem ipsum dolor sit amet, consectetur adipisicing
    elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
    ut aliquip ex ea commodo consequat. Duis aute irure dolor in
    reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa
    qui officia deserunt mollit anim id est laborum
</blockquote>
<h3>
    Permissions
</h3>
<p>
    Lorem ipsum dolor sit amet, consectetur adipisicing
    elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
    ut aliquip ex ea commodo consequat. Duis aute irure dolor in
    reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa
    qui officia deserunt mollit anim id est laborum
</p>
<h4>
    General Concept
</h4>
<p>
    Lorem ipsum dolor sit amet, consectetur adipisicing
    elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
    ut aliquip ex ea commodo consequat. Duis aute irure dolor in
    reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa
    qui officia deserunt mollit anim id est laborum
</p>
<h4>
    Exceptions
</h4>
<p>
    Lorem ipsum dolor sit amet, consectetur adipisicing
    elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
    ut aliquip ex ea commodo consequat. Duis aute irure dolor in
    reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
    pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa
    qui officia deserunt mollit anim id est laborum
</p>
<h3>
    Usability
</h3>
<ul>
<li>
    Point 1
</li>
<li>
    Point 2
</li>
<li>
<strong>
    Point 3
</strong>
<br />
<span>
    Has some implications.
</span>
</li>
</ul>

Generating XML

As an added bonus, you can even make XML documents with KvzHTML

<?php
error_reporting(E_ALL);
require_once 'KvzHTML.php';

$H = new KvzHTML(array(
  'xml' => true,
));

echo $H->xml(
  $H->auth(
      $H->username('kvz') .
      $H->api_key(sha1('xxxxxxxxxxxxxxxx'))
  ) .
  $H->server_reboot(
      $H->dry_run(null) .
      $H->hostname('www1.example.com') .
      $H->server_id(888)
  )
);
?>

Result

<?xml version='1.0' encoding='UTF-8'?>
<auth>
    <username>kvz</username>
    <api_key>a7a7c2e911a47b967d34b5a8807c040e9d167815</api_key>
</auth>
<server_reboot>
    <dry_run />
    <hostname>www1.example.com</hostname>
    <server_id>888</server_id>
</server_reboot>

Special Functions

I had most fun with KvzHTML while generating HTML that would be converted to PDF afterwards. These were massive HTML documents that no designer was going to touch. So no need to keep the HTML plain for Dreamweaver or anything.

In this light, some of the functions below will make more sense.

<?php
error_reporting(E_ALL);
require_once 'KvzHTML.php';

// I find it easy to work with 2 instances.
//    One that will echo directly: $E
// and One that supports nesting: $H
$H = new KvzHTML();
$E = new KvzHTML(array('echo' => true, 'buffer' => true, 'tidy' => true));

// To save you even more typing. The following tags
// have an inconsistent interface:
// a, img, css, js

$E->html();
  $E->head(
      $H->title('Report') .
      $H->style('
         div.page {
             font-family: helvetica;
             font-size: 12px;
             page-break-after: always;
             min-height: 1220px;
             width: 830px;
         }
     ') .
      $H->css('/css/style.js') .
      $H->js('/js/jquery.js')
  );

  // Page 1
  $E->page(true, array('style' => array(
      'page-break-before' => 'always',
  )));

  $E->h1('Report') .

      $E->p(
          $H->a('https://true.nl', 'Visit our homepage') .
          $H->img('/assets/images/posts/2009-10-11-generate-html-with-php-0.png')
      );

      $E->ul(
          $H->li('Health') .
          $H->li('Uptime') .
          $H->li('Logs') .
          $H->li('Recommendations')
      );
  $E->page(false);

  // Page 2
  $E->page();
      $E->float($H->img('https://en.gravatar.com/userimage/3781109/874501515fabcf6069d64c626cf8e4f6.png'));
      $E->float($H->img('https://en.gravatar.com/userimage/3781109/874501515fabcf6069d64c626cf8e4f6.png'));
      $E->clear();
  $E->page(false);

  // Page 3
  $E->page(
      $H->h2('Warnings') .
      $H->p('Disk space', array('class' => 'warning'))
  );

$E->html(false);

echo $E->getBuffer();
?>

Result

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
    <head>
        <title>
            Report
        </title>
        <style type="text/css">

                    div.page {
                        font-family: helvetica;
                        font-size: 12px;
                        page-break-after: always;
                        min-height: 1220px;
                        width: 830px;
                    }
        </style>
        <link type='text/css' rel='stylesheet' href='/css/style.js'>
        <script type='text/javascript' src='/js/jquery.js'>
</script>
        <style type="text/css">
div.c3 {clear: both;}
        div.c2 {float: left;}
        div.c1 {page-break-before: always;}
        </style>
    </head>
    <body>
        <div class='page c1'>
            <h1>
                Report
            </h1>
            <p>
                <a href='https://true.nl'>Visit our homepage</a> <img src='/assets/images/posts/2009-10-11-generate-html-with-php-0.png'>
            </p>
            <ul>
                <li>Health
                </li>
                <li>Uptime
                </li>
                <li>Logs
                </li>
                <li>Recommendations
                </li>
            </ul>
        </div>
        <div class='page'>
            <div class='c2'>
                <img src='https://en.gravatar.com/userimage/3781109/874501515fabcf6069d64c626cf8e4f6.png'>
            </div>
            <div class='c2'>
                <img src='https://en.gravatar.com/userimage/3781109/874501515fabcf6069d64c626cf8e4f6.png'>
            </div>
            <div class='c3'></div>
        </div>
        <div class='page'>
            <h2>
                Warnings
            </h2>
            <p class='warning'>
                Disk space
            </p>
        </div>
    </body>
</html>

And There You Have it..

Get it while it's hot, and if you have improvements: Leave me a comment or even better: GitHub me some patches! :)