3 namespace RemexHtml\Serializer;
4 use RemexHtml\Tokenizer\Attribute;
5 use RemexHtml\Tokenizer\Attributes;
6 use RemexHtml\Tokenizer\PlainAttributes;
7 use RemexHtml\HTMLData;
8 use RemexHtml\DOM\DOMFormatter;
9 use RemexHtml\DOM\DOMUtils;
12 * A Formatter which is used to format documents in (almost) the way they
13 * appear in the html5lib tests. A little bit of post-processing is required
14 * in the PHPUnit tests.
16 class TestFormatter implements Formatter, DOMFormatter {
17 private static $attrNamespaces = [
18 HTMLData::NS_XML => 'xml',
19 HTMLData::NS_XLINK => 'xlink',
20 HTMLData::NS_XMLNS => 'xmlns',
23 public function startDocument( $fragmentNamespace, $fragmentName ) {
27 public function doctype( $name, $public, $system ) {
28 $ret = "<!DOCTYPE $name";
29 if ( $public !== '' || $system !== '' ) {
30 $ret .= " \"$public\" \"$system\"";
36 public function characters( SerializerNode $parent, $text, $start, $length ) {
37 return $this->formatCharacters( substr( $text, $start, $length ) );
40 private function formatCharacters( $text ) {
42 str_replace( "\n", "<EOL>", $text ) .
46 public function element( SerializerNode $parent, SerializerNode $node, $contents ) {
47 return $this->formatElement( $node->namespace, $node->name,
48 $node->attrs->getObjects(), $contents );
51 private function formatElement( $namespace, $name, $attrs, $contents ) {
52 $name = DOMUtils::uncoerceName( $name );
53 if ( $namespace === HTMLData::NS_HTML ) {
55 } elseif ( $namespace === HTMLData::NS_SVG ) {
56 $tagName = "svg $name";
57 } elseif ( $namespace === HTMLData::NS_MATHML ) {
58 $tagName = "math $name";
62 $ret = "<$tagName>\n";
63 $sortedAttrs = $attrs;
64 ksort( $sortedAttrs, SORT_STRING );
65 foreach ( $sortedAttrs as $attrName => $attr ) {
66 $localName = DOMUtils::uncoerceName( $attr->localName );
67 if ( $attr->namespaceURI === null
68 || isset( $attr->reallyNoNamespace )
71 } elseif ( isset( self::$attrNamespaces[$attr->namespaceURI] ) ) {
72 $prefix = self::$attrNamespaces[$attr->namespaceURI] . ' ';
74 $ret .= " $prefix$localName=\"{$attr->value}\"\n";
76 if ( $contents !== null && $contents !== '' ) {
77 $contents = preg_replace( '/^/m', ' ', $contents );
81 if ( $namespace === HTMLData::NS_HTML && $name === 'template' ) {
82 if ( $contents === '' ) {
83 $contents = " content\n";
85 $contents = " content\n" . preg_replace( '/^/m', ' ', $contents );
92 public function comment( SerializerNode $parent, $text ) {
93 return $this->formatComment( $text );
96 private function formatComment( $text ) {
97 return "<!-- $text -->\n";
100 public function formatDOMNode( \DOMNode $node ) {
102 if ( $node->firstChild ) {
103 foreach ( $node->childNodes as $child ) {
104 $contents .= $this->formatDOMNode( $child );
108 switch ( $node->nodeType ) {
109 case XML_ELEMENT_NODE:
110 return $this->formatDOMElement( $node, $contents );
112 case XML_DOCUMENT_NODE:
113 case XML_DOCUMENT_FRAG_NODE:
117 case XML_CDATA_SECTION_NODE:
118 return $this->formatCharacters( $node->data );
120 case XML_COMMENT_NODE:
121 return $this->formatComment( $node->data );
123 case XML_DOCUMENT_TYPE_NODE:
124 return $this->doctype( $node->name, $node->publicId, $node->systemId );
132 public function formatDOMElement( \DOMElement $node, $content ) {
134 foreach ( $node->attributes as $attr ) {
136 switch ( $attr->namespaceURI ) {
137 case HTMLData::NS_XML:
139 $qName = 'xml:' . $attr->localName;
141 case HTMLData::NS_XMLNS:
142 if ( $attr->localName === 'xmlns' ) {
146 $qName = 'xmlns:' . $attr->localName;
149 case HTMLData::NS_XLINK:
151 $qName = 'xlink:' . $attr->localName;
154 if ( strlen( $attr->prefix ) ) {
155 $qName = $attr->prefix . ':' . $attr->localName;
157 $prefix = $attr->prefix;
158 $qName = $attr->localName;
162 $attrs[$qName] = new Attribute( $qName, $attr->namespaceURI, $prefix,
163 $attr->localName, $attr->value );
166 return $this->formatElement( $node->namespaceURI, $node->nodeName, $attrs, $content );