1<?php 2 3namespace Sabre\Xml\Element; 4 5use Sabre\Xml\Reader; 6use Sabre\Xml\Writer; 7use Sabre\Xml\Element; 8 9/** 10 * The XmlFragment element allows you to extract a portion of your xml tree, 11 * and get a well-formed xml string. 12 * 13 * This goes a bit beyond `innerXml` and friends, as we'll also match all the 14 * correct namespaces. 15 * 16 * Please note that the XML fragment: 17 * 18 * 1. Will not have an <?xml declaration. 19 * 2. Or a DTD 20 * 3. It will have all the relevant xmlns attributes. 21 * 4. It may not have a root element. 22 */ 23class XmlFragment implements Element { 24 25 protected $xml; 26 27 function __construct($xml) { 28 29 $this->xml = $xml; 30 31 } 32 33 function getXml() { 34 35 return $this->xml; 36 37 } 38 39 /** 40 * The xmlSerialize metod is called during xml writing. 41 * 42 * Use the $writer argument to write its own xml serialization. 43 * 44 * An important note: do _not_ create a parent element. Any element 45 * implementing XmlSerializble should only ever write what's considered 46 * its 'inner xml'. 47 * 48 * The parent of the current element is responsible for writing a 49 * containing element. 50 * 51 * This allows serializers to be re-used for different element names. 52 * 53 * If you are opening new elements, you must also close them again. 54 * 55 * @param Writer $writer 56 * @return void 57 */ 58 function xmlSerialize(Writer $writer) { 59 60 $reader = new Reader(); 61 62 // Wrapping the xml in a container, so root-less values can still be 63 // parsed. 64 $xml = <<<XML 65<?xml version="1.0"?> 66<xml-fragment xmlns="http://sabre.io/ns">{$this->getXml()}</xml-fragment> 67XML; 68 69 $reader->xml($xml); 70 71 $elementNamespace = null; 72 73 while ($reader->read()) { 74 75 if ($reader->depth < 1) { 76 // Skipping the root node. 77 continue; 78 } 79 80 switch ($reader->nodeType) { 81 82 case Reader::ELEMENT : 83 $writer->startElement( 84 $reader->getClark() 85 ); 86 $empty = $reader->isEmptyElement; 87 while ($reader->moveToNextAttribute()) { 88 switch ($reader->namespaceURI) { 89 case '' : 90 $writer->writeAttribute($reader->localName, $reader->value); 91 break; 92 case 'http://www.w3.org/2000/xmlns/' : 93 // Skip namespace declarations 94 break; 95 default : 96 $writer->writeAttribute($reader->getClark(), $reader->value); 97 break; 98 } 99 } 100 if ($empty) { 101 $writer->endElement(); 102 } 103 break; 104 case Reader::CDATA : 105 case Reader::TEXT : 106 $writer->text( 107 $reader->value 108 ); 109 break; 110 case Reader::END_ELEMENT : 111 $writer->endElement(); 112 break; 113 114 } 115 116 } 117 118 } 119 120 /** 121 * The deserialize method is called during xml parsing. 122 * 123 * This method is called statictly, this is because in theory this method 124 * may be used as a type of constructor, or factory method. 125 * 126 * Often you want to return an instance of the current class, but you are 127 * free to return other data as well. 128 * 129 * You are responsible for advancing the reader to the next element. Not 130 * doing anything will result in a never-ending loop. 131 * 132 * If you just want to skip parsing for this element altogether, you can 133 * just call $reader->next(); 134 * 135 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to 136 * the next element. 137 * 138 * @param Reader $reader 139 * @return mixed 140 */ 141 static function xmlDeserialize(Reader $reader) { 142 143 $result = new self($reader->readInnerXml()); 144 $reader->next(); 145 return $result; 146 147 } 148 149} 150