Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 280495
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 12, 20262026-05-12T05:07:24+00:00 2026-05-12T05:07:24+00:00

I ended up creating my own HTML class, mainly to keep all our output

  • 0

I ended up creating my own HTML class, mainly to keep all our output consistent. The code is below.

<?php

/**
 * A class to generate html tags
 * @author Glen Solsberry
 */
class HTML {

    private $isOpen;
    /**
     * holds all information about tags
     * @var $tags array
     */
    private $tags;
    private $current_tag = 0;

    private $depth = 0;
    private $output = "";

    private $separator = " ";
    private $pretty_print = true;

    /**
     * Set the pretty_print status
     * @author Glen Solsberry
     */
    public function setPretty($new_value) {
        $this->pretty_print = (bool)$new_value;
    }

    /**
     * Set the "separator" (the string that will be printed before tags on a new line)
     * @author Glen Solsberry
     */
    public function setSeparator($new_value) {
        $this->separator = $new_value;
    }

    /**
     * add a tag to the "DOM"
     * @author Glen Solsberry
     */
    public function tag($tag) {
        $this->updateChildrenAndParent();

        $this->tags[$this->current_tag]['tag'] = $tag;
        $this->tags[$this->current_tag]['open'] = true;

        return $this;
    }

    /**
     * set an attribute on the current tag
     * @author Glen Solsberry
     */
    public function attr($key, $value) {
        $this->tags[$this->current_tag]['attrs'][$key] = $value;

        return $this;
    }

    /**
     * close the current tag, if it's open.  if the tag is already closed, we work back up the chain to determine which tag needs to be closed.
     * @author Glen Solsberry
     */
    public function close() {
        if ($this->isOpen($this->current_tag)) {
            $this->tags[$this->current_tag]['open'] = false;
        } else {
            // work backwards till we find the first open tag, and close it
            for($previous_id = $this->current_tag - 1; $previous_id >= 0; $previous_id--) {
                if ($this->isOpen($previous_id)) {
                    $this->tags[$previous_id]['open'] = false;
                    break;
                }
            }
        }

        return $this;
    }

    /**
     * Create a text node
     * @author Glen Solsberry
     */
    public function text($text) {
        $this->updateChildrenAndParent();

        $this->tags[$this->current_tag]['text'] = $text;
        $this->tags[$this->current_tag]['open'] = false;

        return $this;
    }

    /**
     * Updates children and parent information, so that all tags link properly.
     * Duplicates will be handled later.
     * @author Glen Solsberry
     */
    private function updateChildrenAndParent() {
        // if the current tag is still open, then this needs to be a child of it
        if ($this->isOpen($this->current_tag)) {
            $this->tags[$this->current_tag]['children'][] = count($this->tags);
        }
        $this->current_tag = count($this->tags);
        // default value for this tags parent
        $this->tags[$this->current_tag]['parent'] = $this->current_tag - 1;

        for($parent_id = $this->current_tag - 1; $parent_id >= 0; $parent_id--) {
            // is the parent still open?
            if ($this->isOpen($parent_id)) {
                $this->tags[$this->current_tag]['parent'] = $parent_id;
                $this->tags[$parent_id]['children'][] = $this->current_tag;
                break;
            }
        }
    }

    /**
     * Determines whether the passed tag_id is open
     * @author Glen Solsberry
     */
    private function isOpen($tag_id) {
        return (bool)$this->tags[$tag_id]['open'];
    }

    /**
     * Determines whether the passed tag_id is closed
     * @author Glen Solsberry
     */
    private function isClosed($tag_id) {
        return (bool)!$this->tags[$tag_id]['open'];
    }

    /**
     * Generates a single tag's html represenation.  Called recursively if there are children
     * @author Glen Solsberry
     */
    private function generateTag($tag_id) {
        $current_tag = $this->tags[$tag_id];

        if (isset($current_tag['tag'])) {
            $this->output .= "<" . $current_tag['tag'];
            if (isset($current_tag['attrs'])) {
                ksort($current_tag['attrs']);
                foreach($current_tag['attrs'] as $key => $value) {
                    $this->output .= " " . $key . "=\"" . htmlspecialchars($value) . "\"";
                }
            }
            $this->output .= $this->pretty_print(">");

            if (isset($current_tag['children']) && count($current_tag['children']) > 0) {
                $children = array_unique($current_tag['children']);
                foreach($children as $position => $child_id) {
                    $this->depth++;
                    $this->generateTag($child_id);
                    $this->depth--;
                }
                $this->output .= $this->pretty_print("</" . $current_tag['tag'] . ">");
            } else {
                $this->output .= $this->pretty_print("</" . $current_tag['tag'] . ">");
                $this->depth--;
            }
        } else if (isset($current_tag['text'])) {
            $this->output .= $current_tag['text'];
        }
    }

    /**
     * Pretty prints the output.  Uses newlines and line starters.
     * @author Glen Solsberry
     */
    private function pretty_print($string) {
        $output = "";
        if ($this->pretty_print == true && $this->depth > 0) {
            $output .= str_repeat($this->separator, $this->depth);
        }
        $output .= $string;
        if ($this->pretty_print == true) {
            $output .= "\n";
        }
        return $output;
    }

    function __toString() {
        $this->generateTag(0);
        return $this->output;
    }

}

?>

I built some tests to make sure everything comes out properly (and to be a good PHP dev)…

<?php
require_once 'PHPUnit/Framework.php';
require_once 'PHPUnit/Extensions/OutputTestCase.php';
require_once '../html.class.inc';

class HTMLTest extends PHPUnit_Extensions_OutputTestCase
{
    public function testOutputLooksCorrect1() {
        $html = $this->sharedFixture;

        $html->tag("html");
        $html->close();

        $this->expectOutputString('<html></html>');
        print $html;
    }

    public function testOutputLooksCorrect2() {
        $html = $this->sharedFixture;

        $html->tag("html");
            $html->tag("head");
            $html->close();
        $html->close();

        $this->expectOutputString('<html><head></head></html>');
        print $html;
    }

    public function testOutputLooksCorrect3() {
        $html = $this->sharedFixture;

        $html->tag("html");
            $html->tag("head");
                $html->tag("link")->attr("rel", "stylesheet")->attr("href", "testOutputLooksCorrect.css")->attr("type", "text/css")->close();
            $html->close();
        $html->close();

        $this->expectOutputString('<html><head><link href="testOutputLooksCorrect.css" rel="stylesheet" type="text/css"></link></head></html>');
        print $html;
    }

    public function testOutputLooksCorrect4() {
        $html = $this->sharedFixture;

        $html->tag("html");
            $html->tag("head");
                $html->tag("link")->attr("href", "testOutputLooksCorrect.css")->attr("rel", "stylesheet")->attr("type", "text/css")->close();
            $html->close();
        $html->close();

        $this->expectOutputString('<html><head><link href="testOutputLooksCorrect.css" rel="stylesheet" type="text/css"></link></head></html>');
        print $html;
    }

    public function testOutputLooksCorrect5() {
        $html = $this->sharedFixture;

        $html->tag("html");
            $html->tag("head");
                $html->tag("link")->attr("type", "text/css")->attr("href", "testOutputLooksCorrect.css")->attr("rel", "stylesheet")->close();
            $html->close();
        $html->close();

        $this->expectOutputString('<html><head><link href="testOutputLooksCorrect.css" rel="stylesheet" type="text/css"></link></head></html>');
        print $html;
    }

    public function testOutputLooksCorrect10() {
        $html = $this->sharedFixture;

        $html->tag("html");
            $html->tag("head");
                $html->tag("link")->attr("rel", "stylesheet")->attr("href", "testOutputLooksCorrect.css")->attr("type", "text/css")->close();
            $html->close();
            $html->tag("body");
            $html->close();
        $html->close();

        $this->expectOutputString('<html><head><link href="testOutputLooksCorrect.css" rel="stylesheet" type="text/css"></link></head><body></body></html>');
        print $html;
    }

    public function testOutputLooksCorrect11() {
        $html = $this->sharedFixture;

        $html->tag("html");
            $html->tag("head");
                $html->tag("link")->attr("rel", "stylesheet")->attr("href", "testOutputLooksCorrect.css")->attr("type", "text/css")->close();
            $html->close();
            $html->tag("body");
                $html->text("This is a testOutputLooksCorrect.");
            $html->close();
        $html->close();

        $this->expectOutputString('<html><head><link href="testOutputLooksCorrect.css" rel="stylesheet" type="text/css"></link></head><body>This is a testOutputLooksCorrect.</body></html>');
        print $html;
    }

    public function testOutputLooksCorrect12() {
        $html = $this->sharedFixture;

        $html->text("This is a testOutputLooksCorrect.");

        $this->expectOutputString('This is a testOutputLooksCorrect.');
        print $html;
    }

    public function testOutputLooksCorrect13() {
        $html = $this->sharedFixture;

        $html->tag("head")->close();

        $this->expectOutputString('<head></head>');
        print $html;
    }

    public function testOutputLooksCorrect14() {
        $html = $this->sharedFixture;

        $html->tag("head")->tag("title")->text("This is the title")->close()->close();

        $this->expectOutputString('<head><title>This is the title</title></head>');
        print $html;
    }

    public function testOutputLooksCorrect15() {
        $html = $this->sharedFixture;

        $html->tag("html");
            $html->tag("head");
                $html->tag("link")->attr("rel", "stylesheet")->attr("href", "testOutputLooksCorrect.css")->attr("type", "text/css")->close();
                $html->tag("meta")->attr("http-equiv", "Content-Type")->attr("content", "text/html; charset=utf-8")->close();
            $html->close();
            $html->tag("body");
                $html->text("This is a testOutputLooksCorrect.");
            $html->close();
        $html->close();
        $this->expectOutputString('<html><head><link href="testOutputLooksCorrect.css" rel="stylesheet" type="text/css"></link><meta content="text/html; charset=utf-8" http-equiv="Content-Type"></meta></head><body>This is a testOutputLooksCorrect.</body></html>');
        print $html;
    }

    public function testOutputLooksCorrect16() {
        $html = $this->sharedFixture;

        $html->tag("html");
            $html->tag("head");
                $html->tag("link")->attr("rel", "stylesheet")->attr("href", "testOutputLooksCorrect.css")->attr("type", "text/css")->close();
                $html->tag("meta")->attr("http-equiv", "Content-Type")->attr("content", "text/html; charset=utf-8")->close();
                $html->tag("script")->attr("type", "javascript")->attr("src", "blah.js")->close();
            $html->close();
            $html->tag("body");
                $html->text("This is a testOutputLooksCorrect.");
            $html->close();
        $html->close();

        $this->expectOutputString('<html><head><link href="testOutputLooksCorrect.css" rel="stylesheet" type="text/css"></link><meta content="text/html; charset=utf-8" http-equiv="Content-Type"></meta><script src="blah.js" type="javascript"></script></head><body>This is a testOutputLooksCorrect.</body></html>');
        print $html;
    }

    protected function setUp() {
        $html = new HTML;
        $html->setPretty(0);
        $this->sharedFixture = $html;
    }

    protected function tearDown() {
        $this->sharedFixture = NULL;
    }
}
?>

Everything works properly. However, the time needed is much higher than I’d like. On the order of ~3 seconds for 14k worth of HTML.

After profiling the code, it seems that most of the time is spent in the updateChildrenAndParent. Almost 75% of the entire execution is spent there. Can anyone suggest ways to speed this up, since I don’t want to slow down the site noticably.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-12T05:07:24+00:00Added an answer on May 12, 2026 at 5:07 am

    You should be using real dom methods to parse HTML. They are written in C and will be orders of magnitude faster than anything you can code up in naively.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

After some find and replace refactoring I ended up with this gem: const class
The other day I thought I'd attempt creating the Fibonacci algorithm in my code,
I'm creating a site using PHP and it has to access the Barclaycard ePDQ
I ended up writing a quick little script for this in Python, but I
I ended up doing some emergency PM stuff at work and I would like
Here's an open ended question. I work on a lot of mssql files, and
This is a difficult and open-ended question I know, but I thought I'd throw
In my last job we ended up rewriting the complete ASP.NET stack (forms, controls,
The same way DOS morphed into Windows? We seem to have ended up supporting
Our team has a task system where we post small incremental tasks assigned to

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.