php动态HTML单元格意外生成

I am currently working on interface part a framework to generate HTML tags. Problem i am facing is while generating tags for nested tables and cells. for this purpose, I have one html class which define common expectation of tags. Another class is htmlTable which is inherited from hrml class and customised for table, TR, TD. While i want to add nested tables on a particular cell, the result is unexpected. It generates hundreds of TDs and TRs.

Following code is customised to post here as putting all code will make long and too confusing. And one more thing, I am only concentrated to solve logical error to get exact parent, child and nested elements of HTML tag.

I have tried as much as i could but couldnot get this fixed. This might be easy for some of you. So please advice me your solution. I have got to go to work so will reply only once i get back. Thnak you

<?php
// scHTML.php
class HTML
{
    protected $tag;
    protected $id;

    protected $values;
    protected $childList;

    protected $body;

    public function __construct($tag,$id)
    {
        $this->tag=$tag;
        $value= array('id'=>$id);
        $this->setValues($value);
    }

    // to add nested tag
    public function addChildTag(HTML $child)
    {
        $this->childList[]=$child;
    }

    // to add key and value on opening tag
    public function setValues($values)
    {
        if(count($values)>0)
        {
            foreach($values as $key=>$value)
            {
                $this->values[$key]=$values;
            }
        }
    }

    // value inside tag
    public function setBody($body)
    {
        $this->body=$body;
    }

    // generate HTML code
    public function getHTML()
    {
        $return= '<'.$this->tag;
        if(count($this->values)>0)
        {
            foreach($this->values as $key=>$value)
            {
                $return.=' '.$key.'= "'.$value.'" ';
            }
        }
        $return.= '> 
        ';
        if(count($this->childList)>0)
        {
            $return.=
                            $this->body
                            ;
            foreach($this->childList as $child)
            {
                $return.=
                                $child->getHTML();
            }
        }
        $return.=' </'.$this->tag.'>';
        return $return;
    }

    public function __toString()
    {
        return $this->getHTML();
    }   
}
?>

and here is the inherited class for table

<?php
//scHTMLTable.php
include_once("scHTML.php");
class HTMLTable extends HTML
{
    protected $cells; // logical grid of cells for current table
    protected $row;
    protected $column;

    public function __construct($row,$column,$id)
    {
        $this->row=$row;
        $this->column=$column;
        $this->initiateTable();
        $values['border']='1px';
        $this->setValues($values);

        parent::__construct("table",$id);

    }

    // initaite table cells according to row and column provided
    private function initiateTable()
    {
        $td=new HTML("td",NULL);
        $td->setBody('&nbsp;');
        for($i=1;$i<=$this->row;$i++)
        {
            for($j=1;$j<=$this->column;$j++)
            {
                $this->addCell($i,$j,$td);
            }
        }
    }

    // set child element as body on a cell
    public function addChildOnCell($row,$column,HTML $child)
    {
        $td=$this->cells[$row][$column];
        $td->addChildTag($child);
        $this->addCell($row,$column,$child);
    }

    // set cell 
    public function addCell($row,$column,HTML $cell)
    {
        $this->cells[$row][$column]=$cell;
    }

    // set TR as child and call parent method to generate html code.
    public function getHTML()
    {
        for($i=1;$i<=$this->row;$i++)
        {
            $tr=new HTML("tr",NULL);
            for($j=1;$j<=$this->column;$j++)
            {
                $td=$this->cells[$i][$j];
                $tr->addChildTag($td);
            }
            $this->addChildTag($tr);
        }
        return parent::getHTML();
    }

    public function __toString()
    {
        return $this->getHTML();
    }
}

?>

And here is the implementation.

<?php
include_once("scHTML.php");
include_once("scHTMLTable.php");


$html= new HTML("html",NULL); 
$body=new HTML("body",NULL); 

// three table to be tested
$a= new HTMLTable(3,3,"grandparent");
$b=new HTMLTable(3,3,"parent");
$c=new HTMLTable(1,1,"child");

$b->addChildOnCell(2,2,$c);
$a->addChildOnCell(2,2,$b);

$body->addChildTag($a);
$html->addChildTag($body);

echo $html;  
?>

Output i am looking as source code is somewhere near this

<html>


<body>
<table border="1">
 <tr>
  <td>&nbsp;</td>
  <td>&nbsp;</td>
  <td>&nbsp;</td>
 </tr>
 <tr>
  <td>&nbsp;</td>
  <td><table border="1">
   <tr>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
   </tr>
   <tr>
    <td>&nbsp;</td>
    <td><table border="1">
     <tr>
      <td>&nbsp;</td>
     </tr>
    </table></td>
    <td>&nbsp;</td>
   </tr>
   <tr>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
    <td>&nbsp;</td>
   </tr>
  </table></td>
  <td>&nbsp;</td>
 </tr>
 <tr>
  <td>&nbsp;</td>
  <td>&nbsp;</td>
  <td>&nbsp;</td>
 </tr>
</table>
</body>
</html>

I agree with MrMesees in that I think your table class is doing too much and that each node type is only responsible for generating it's own DOM representation. Any to-string / to-html recursion should only move downward through the DOM, though (no calls to parents for rendering assistance). Here is a quick demo using some of your custom API method names:

  class HTML
  {
    private $tagName;
    protected $attributes = [];
    protected $children = [];
    private $text = '';

    public function __construct($tagName, $text='') {
      $this->tagName = strtolower($tagName);
      $this->text = $text;
    }

    public function addChildTag(HTML $n) {    
      $this->children[] = $n;
      return $this;
    }

    public function getHTML($depth=0) {
      $padding = str_repeat("\t", $depth);
      $result = "$padding<$this->tagName";
      foreach($this->attributes as $k => $v) {
        $result .= " $k=\"".htmlspecialchars($v)."\"";
      }
      if (count($this->children) == 0 && strlen($this->text) == 0) {
        $result .= " />
";
      }      
      else {
        $result .= ">";
        $result .= $this->text;
        if (count($this->children) > 0) $result .= "
";
        foreach($this->children as $c) {
          $result .= $c->getHTML($depth+1);
        }
        $result .= "$padding</$this->tagName>
";
      }
      return $result;
    }

    public function tagName() { return $this->tagName; }

    public function findChild($tagName, $index=0) {
      $nodes = array_filter($this->children, function($e) use ($tagName) {  return $e->tagName() == $tagName;  });
      return count($nodes) ? $nodes[$index] : null;
    }

    public function __toString() { return $this->getHTML(); }
  }

  class HTMLTable extends HTML {
    public function __construct($rows, $cols, $id) {
      parent::__construct("table");
      $this->attributes['id'] = $id;
      $this->attributes['border'] = '1px';
      for($r=0; $r<$rows; $r++) {
        $row = new HTML("tr");
        for($c=0; $c<$cols; $c++) {
          $cell = new HTML("td");
          $row->addChildTag($cell);
        }
        $this->addChildTag($row);
      }
    }

    public function addChildOnCell($row, $col, HTML $child) {
      return $this->findChild("tr", $row-1)->findChild("td", $col-1)->addChildTag($child);
    }
  }

  $html= new HTML("html"); 
  $body=new HTML("body"); 

  // three table to be tested
  $a= new HTMLTable(3,3,"grandparent");
  $b=new HTMLTable(3,3,"parent");
  $c=new HTMLTable(1,1,"child");

  $b->addChildOnCell(2,2,$c);
  $a->addChildOnCell(2,2,$b);

  $body->addChildTag($a);
  $html->addChildTag($body);

  echo $html;

Which results in output:

<html>
    <body>
        <table id="grandparent" border="1px">
            <tr>
                <td />
                <td />
                <td />
            </tr>
            <tr>
                <td />
                <td>
                    <table id="parent" border="1px">
                        <tr>
                            <td />
                            <td />
                            <td />
                        </tr>
                        <tr>
                            <td />
                            <td>
                                <table id="child" border="1px">
                                    <tr>
                                        <td />
                                    </tr>
                                </table>
                            </td>
                            <td />
                        </tr>
                        <tr>
                            <td />
                            <td />
                            <td />
                        </tr>
                    </table>
                </td>
                <td />
            </tr>
            <tr>
                <td />
                <td />
                <td />
            </tr>
        </table>
    </body>
</html>

It's 2015, you should not be manually including files; there are clear coding-standards, so you should have namespaces and register an auto-loader in your main App.

Also as long as you are going to string build in this way, using the __toString() method on objects representing the tables, you should just have other classes for the nested parts, each with their own __toString method; so you can have HTMLElement class (you've got), with table (you have but it's not ideal), table-row, table-cell.

Extending this, you should if you take this route extend all HTMLElements, and have a rawString HTMLElement extended class for when you feel like just getting things done parrot fashion.

  • table row __toString method would just iterate over contained cells and return their __toString methods
  • table would iterate over rows and call their __toString method
  • table cell (the most / least difficult part), would take instances implementing HTMLElement, and iterate over them calling their __toString method

It's a lot cleaner method of working than the current, and then as each outputs it's own DOM, you should be free of the nasty bug you have now.

If the table gets too large, you are going to wish you had just coded a series of views that work with one-level of XYdata, or simplified in another way; but the data structures to this sort of nested view, unless you are working with JSON or XML anyway (in the case of XML, just use XSL); are not going to be nice to retrieve from say SQL Server, MySQL, search upon, or really to do that many useful things with...