Link to home
Start Free TrialLog in
Avatar of Richard Quadling
Richard QuadlingFlag for United Kingdom of Great Britain and Northern Ireland

asked on

PHP Coding layout question.

Hi.

I have just finished creating a class to show a 12 month calendar (primarily to be used as an Absence Map from our HR system).

This is the first real class I have written.

Effectively, you create an instance of the class, use the Add_Legend and Add_Data methods to inject suitable data and then call Show_Calendar to extract the HTML to show it. The HTML contains no formatting, all colouring, etc is CSS based.

All fine and everyone likes it.

My question is about coding the HTML generation.

At the moment, there are a lot of ...

$sResult .= <<< END_HTML
some_html_goes_here
and_here
and_here
END_HTML;

Which is fine, but overall, looks rather messy.

I tried ...

      function Kalendar_ShowLegend()
            {
            if ($this->_bInitialized)
                  {
                  ksort($this->_aLegends);
                  $sResult = "<table class=\"KKey\" cellspacing=\"0\" cellpadding=\"0\"><caption class=\"KKeyCap\">{$this->sLegendTitle}</caption>";
                  $sKeyRow = '';
                  $sValueRow = '';
                  foreach($this->_aLegends as $k => $v)
                        {
                        if ($k != '')
                              {
                              $sKeyValue = "<div class=\"KKeyValIn\" style=\"background-color:{$v['Colour']};\">{$v['Additional']}</div>";
                              if ($this->bLegendPortrait == True)
                                    {
                                    $sResult .= "<tr><th class=\"KKeyKey\">$k</th><td class=\"KKeyValOut\">$sKeyValue</td></tr>";
                                    }
                              else
                                    {
                                    $sKeyRow .= "<th class=\"KKeyKey\">$k</th>";
                                    $sValueRow .= "<td class=\"KKeyValOut\">$sKeyValue</td>";
                                    }
                              }
                        }
                  if ($this->bLegendPortrait == False)
                        {
                        $sResult .= "<tr>$sKeyRow</tr><tr>$sValueRow</tr>";
                        }
                  $sResult .= "</table>";
                  return $sResult;
                  }
            }


which is a little neater, but having to do all the escapes was a pain.

I was considering using some sort of templating system, but that, for this project, is overkill and not appropriate. How many ways can you show a year calendar? Months along the top and date down the side looks REALLY strange!

Basically, coding designs for code which generates fixed HTML output.

Suggestions please.

Regards,

Richard Quadling.
Avatar of ashoooo
ashoooo

I would escape out of the <?php ?> tag and write the HTML directly... something like this...

<?php
...
   if(...)
   {
   }
   else
   {
?>

   <p align="center"> This is NOT inside the PHP tag</p>

<?php
   }
?>
ASKER CERTIFIED SOLUTION
Avatar of AlanJDM
AlanJDM

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Does this have to be XHTML Complant? If not, you can put the attributes in single quotes, instead of double quotes.

Your first assignment becomes:
 $sResult = "<table class='KKey' cellspacing='0' cellpadding='0'><caption class='KKeyCap'>{$this->sLegendTitle}</caption>";

Alternatively you could map some other character instead of the double quote, it would have to be a character that had no other use, lets say for example the | symbol.

Your first assignment would then become

 $sResult = map("<table class=|KKey| cellspacing=|0| cellpadding=|0|><caption class=|KKeyCap|>{$this->sLegendTitle}</caption>");

you would define the function map as follows:

function map(&$my_string){
     strtr($map, "\"", "|");
}
Correction: Passing a constant string, shouldn't be done by reference.

change the above map function to

function map($my_string){
     strtr($map, "\"", "|");
     return $map;
}

Actually come to think of it even if this DOES have to be XHTML Compliant you could map the single quotes to double quotes. It would be different from the above map though, because you would have to only map quotes that are inside tags, and not map quotes that are escaped.

That would require regex

I'm not sure of the exact syntax, but you want to match any single quotes inside between '<' and '>'  that is not escaped.

Having that said if this is for an internal project, chances are that it won't need to be XHTML compliant, and you can just use the single quotes.
Or download a small tool which takes an HTML statement as input and gives out PHP echo statements as output. You can even write one on your own, just replace " by \"
Well my prefered method is this --

create a page that is pure PHP
Create a page that is pure HTML with a php variable for a placeholder for the dynamic string data

Then at the end of your php page, make use of the eval() function in php and presto chango - a dynamic page is generated with out having complex php in the middle of your HTML.

And whala no need to keep escaping in and out of PHP to generate a good webpage - for code

For more information on doing this, check out this question as I go a bit more detailed into ways to set the code up.
https://www.experts-exchange.com/questions/20785310/php-template-system.html

Thanks and have fun making your PHP files cleaner,
red010knight
Hey richard,
fun question.  First off, I agree with Alan, use single-quotes when you're outputting HTML, and just use the concat dot to group things together.  Single quotes are a teeny bit faster than double-quotes anyway, since there is no interpretation going on in there.  Only annoying thing is if you like to output newlines.

Also, you might consider echoing the HTML instead of returning it.  I know, I know, a big golden rule used to be "always return, never output" but I think output buffering has negated that, since if you really need the output of a script you can make a buffer to get it.  Again, this is a little cleaner, and can also give you a small speed boost.

Last idea:
Maybe you could use a domxml-style API for generating your HTML.  So instead of building up a big HTML string, you create a tree object with tag nodes in it.  One caveat:  this pretty effectively negates any previous speed benefit from my first 2 ideas ;)

Maybe you could make dumbed-down html_tag function that you pass the args in to, which outputs an HTML tag.  I could see this being useful in tons of places.  Something like this:

<?php
function html_tag($tagName, $attrs) {
        $args = func_get_args();
        $out = '<' . array_shift($args);
        while ($args) {
                $out .= ' ' . array_shift($args) . '="' . array_shift($args) . '"';
        }
        $out .= '>';
        return $out;
}

echo html_tag('table', 'border', 1, 'cellpadding', 2, 'cellspacing', 123, 'name', 'foo');
?>

I've been fooling around with a node-based HTML-building class after my post yesterday.  Here's the code that you'd use to generate the table using this system (apologies for mangling your brace style).  I'll post the class separately.

function Kalendar_ShowLegend() {
        if ($this->_bInitialized) {
                ksort($this->_aLegends);
                $sResult = new HtmlNode('table', null, 'class', 'KKey', 'cellspacing', 0, 'cellpadding', 0);
                $sResult->addNode('caption', $this->sLegendTitle, 'class', 'KKeyCap');
                $keyRow =& $sResult->addNode('tr');
                if ($this->bLegendPortrait) {
                        $valueRow =& $sResult->addNode('tr'); // append value stuff to a new row
                } else {
                        $valueRow =& $keyRow; // append value stuff to the same row as the keys
                }
                foreach($this->_aLegends as $k => $v) {
                        if ($k == '') continue;
                        $keyRow->addNode('th', $k, 'class', 'KKeyKey');
                        $valueCell =& $valueRow->addNode('td', null, 'class', 'KKeyValout');
                        $valueCell->addNode('div', $v['Additional'], 'class', 'KKeyValIn', 'style', 'background-color:' . $v['Colour']);
                }      
                return $sResult->toString();
        }      
}      
There's no documentation in place yet, but the key function is addNode(), which takes the same arguments as the constructor.  First param is tag name, second is any cdata content, and any remaining args are used to build the attr array.

<?php
class HtmlNode {
      var $_tagName;
      var $_attrs;
      var $_contents;

      function HtmlNode($tagName, $cdata=null, $attrs=null) {
            $this->_tagName = $tagName;
            $this->_contents = array();
            if ($cdata!==null) $this->addCdata($cdata);
            if (is_array($attrs)) {
                  $this->_attrs = $attrs;
            } else if ($attrs !== null) {
                  $this->_attrs = HtmlNode::_attributesFromArgs(func_get_args());
            }
      }

      function _attributesFromArgs($args) {
            array_shift($args);
            array_shift($args);
            $attrs = array();
            while($args) {
                  $attrs[array_shift($args)] = array_shift($args);
            }
            return $attrs;
      }

      function &addNode($childNode) {
            if (!is_object($childNode)) {
                  // use function arguments to call constructor for a new HtmlNode object
                  $args = func_get_args();
                  $cdata = $args[1];
                  $attrs = HtmlNode::_attributesFromArgs($args);
                  $childNode = new HtmlNode($childNode, $cdata, $attrs);
            }
            $this->_contents[] =& $childNode;
            return $childNode;
      }

      function addCdata($cdata) {
            $this->_contents[] = $cdata;
      }

      function toString() {
            $out = '<' . $this->_tagName;
            if ($this->_attrs) {
                  foreach($this->_attrs AS $key=>$value) {
                        $out .= ' ' . $key . '="' . $value . '"';
                  }
            }
            if (count($this->_contents) == 0) {
                  $out .= ' />';
            } else {
                  $out .= '>';
                  foreach($this->_contents AS $child) {
                        if (is_object($child)) {
                              $out .= $child->toString();
                        } else {
                              $out .= $child;
                        }
                  }
                  $out .= '</' . $this->_tagName . '>';
            }
            return $out;
      }
}
?>