Solved

PHP Coding layout question.

Posted on 2003-11-10
10
311 Views
Last Modified: 2006-11-17
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.
0
Comment
Question by:RQuadling
  • 3
  • 3
  • 2
  • +2
10 Comments
 
LVL 3

Expert Comment

by:ashoooo
ID: 9716571
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
   }
?>
0
 
LVL 9

Accepted Solution

by:
AlanJDM earned 50 total points
ID: 9717045
Im not sure my way is any better, but at least I don't have to escape anything. By using single quotes for your PHP strings, you then dont have to escape the double quotes in the string, of course, now you must concatenate all of the variables. Just another way of doing it.

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;
  }
}


Alan
0
 
LVL 6

Expert Comment

by:aolXFT
ID: 9717396
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, "\"", "|");
}
0
 
LVL 6

Expert Comment

by:aolXFT
ID: 9717468
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.
0
 
LVL 3

Expert Comment

by:ashoooo
ID: 9717574
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 \"
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 3

Expert Comment

by:ashoooo
ID: 9717592
0
 
LVL 3

Expert Comment

by:red010knight
ID: 9718308
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.
http://www.experts-exchange.com/Web/Web_Languages/PHP/Q_20785310.html

Thanks and have fun making your PHP files cleaner,
red010knight
0
 
LVL 11

Expert Comment

by:shmert
ID: 9719265
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');
?>

0
 
LVL 11

Expert Comment

by:shmert
ID: 9723975
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();
        }      
}      
0
 
LVL 11

Expert Comment

by:shmert
ID: 9723988
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;
      }
}
?>
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction HTML checkboxes provide the perfect way for a web developer to receive client input when the client's options might be none, one or many.  But the PHP code for processing the checkboxes can be confusing at first.  What if a checkbox is…
Deprecated and Headed for the Dustbin By now, you have probably heard that some PHP features, while convenient, can also cause PHP security problems.  This article discusses one of those, called register_globals.  It is a thing you do not want.  …
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.

863 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now