/** * Process the start tag. * * @todo * - XMLNS namespace handling (we need to parse, even if it's not valid) * - XLink, MathML and SVG namespace handling * - Omission rules: 8.1.2.4 Optional tags */ public function startTag($name, $attributes = array(), $selfClosing = FALSE) { // fprintf(STDOUT, $name); $lname = $this->normalizeTagName($name); // Make sure we have an html element. if (!$this->doc->documentElement && $name !== 'html') { $this->startTag('html'); } // Set quirks mode if we're at IM_INITIAL with no doctype. if ($this->insertMode == static::IM_INITIAL) { $this->quirks = TRUE; $this->parseError("No DOCTYPE specified."); } // SPECIAL TAG HANDLING: // Spec says do this, and "don't ask." if ($name == 'image') { $name = 'img'; } // Autoclose p tags where appropriate. if ($this->insertMode >= static::IM_IN_BODY && Elements::isA($name, Elements::AUTOCLOSE_P)) { $this->autoclose('p'); } // Set insert mode: switch ($name) { case 'html': $this->insertMode = static::IM_BEFORE_HEAD; break; case 'head': if ($this->insertMode > static::IM_BEFORE_HEAD) { $this->parseError("Unexpected head tag outside of head context."); } else { $this->insertMode = static::IM_IN_HEAD; } break; case 'body': $this->insertMode = static::IM_IN_BODY; break; case 'svg': $this->insertMode = static::IM_IN_SVG; break; case 'math': $this->insertMode = static::IM_IN_MATHML; break; case 'noscript': if ($this->insertMode == static::IM_IN_HEAD) { $this->insertMode = static::IM_IN_HEAD_NOSCRIPT; } break; } // Special case handling for SVG. if ($this->insertMode == static::IM_IN_SVG) { $lname = Elements::normalizeSvgElement($lname); } $ele = $this->doc->createElement($lname); foreach ($attributes as $aName => $aVal) { if ($this->insertMode == static::IM_IN_SVG) { $aName = Elements::normalizeSvgAttribute($aName); } elseif ($this->insertMode == static::IM_IN_MATHML) { $aName = Elements::normalizeMathMlAttribute($aName); } $ele->setAttribute($aName, $aVal); // This is necessary on a non-DTD schema, like HTML5. if ($aName == 'id') { $ele->setIdAttribute('id', TRUE); } } // Some elements have special processing rules. Handle those separately. if ($this->rules->hasRules($name)) { $this->current = $this->rules->evaluate($ele, $this->current); } else { $this->current->appendChild($ele); // XXX: Need to handle self-closing tags and unary tags. if (!Elements::isA($name, Elements::VOID_TAG)) { $this->current = $ele; } } // This is sort of a last-ditch attempt to correct for cases where no head/body // elements are provided. if ($this->insertMode <= static::IM_BEFORE_HEAD && $name != 'head' && $name != 'html') { $this->insertMode = static::IM_IN_BODY; } // Return the element mask, which the tokenizer can then use to set // various processing rules. return Elements::element($name); }
protected function attrs($ele) { // FIXME: Needs support for xml, xmlns, xlink, and namespaced elements. if (!$ele->hasAttributes()) { return $this; } // TODO: Currently, this always writes name="value", and does not do // value-less attributes. $map = $ele->attributes; $len = $map->length; for ($i = 0; $i < $len; ++$i) { $node = $map->item($i); $val = $this->enc($node->value, TRUE); // XXX: The spec says that we need to ensure that anything in // the XML, XMLNS, or XLink NS's should use the canonical // prefix. It seems that DOM does this for us already, but there // may be exceptions. $name = $node->name; // Special handling for attributes in SVG and MathML. // Using if/elseif instead of switch because it's faster in PHP. if ($this->outputMode == static::IM_IN_SVG) { $name = Elements::normalizeSvgAttribute($name); } elseif ($this->outputMode == static::IM_IN_MATHML) { $name = Elements::normalizeMathMlAttribute($name); } $this->wr(' ')->wr($name); if (isset($val) && $val !== '') { $this->wr('="')->wr($val)->wr('"'); } } }