1<?php 2 3namespace Sabre\Xml\Element; 4 5use Sabre\Xml\Element; 6use Sabre\Xml\Reader; 7use Sabre\Xml\Writer; 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 while ($reader->read()) { 72 73 if ($reader->depth < 1) { 74 // Skipping the root node. 75 continue; 76 } 77 78 switch ($reader->nodeType) { 79 80 case Reader::ELEMENT : 81 $writer->startElement( 82 $reader->getClark() 83 ); 84 $empty = $reader->isEmptyElement; 85 while ($reader->moveToNextAttribute()) { 86 switch ($reader->namespaceURI) { 87 case '' : 88 $writer->writeAttribute($reader->localName, $reader->value); 89 break; 90 case 'http://www.w3.org/2000/xmlns/' : 91 // Skip namespace declarations 92 break; 93 default : 94 $writer->writeAttribute($reader->getClark(), $reader->value); 95 break; 96 } 97 } 98 if ($empty) { 99 $writer->endElement(); 100 } 101 break; 102 case Reader::CDATA : 103 case Reader::TEXT : 104 $writer->text( 105 $reader->value 106 ); 107 break; 108 case Reader::END_ELEMENT : 109 $writer->endElement(); 110 break; 111 112 } 113 114 } 115 116 } 117 118 /** 119 * The deserialize method is called during xml parsing. 120 * 121 * This method is called statictly, this is because in theory this method 122 * may be used as a type of constructor, or factory method. 123 * 124 * Often you want to return an instance of the current class, but you are 125 * free to return other data as well. 126 * 127 * You are responsible for advancing the reader to the next element. Not 128 * doing anything will result in a never-ending loop. 129 * 130 * If you just want to skip parsing for this element altogether, you can 131 * just call $reader->next(); 132 * 133 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to 134 * the next element. 135 * 136 * @param Reader $reader 137 * @return mixed 138 */ 139 static function xmlDeserialize(Reader $reader) { 140 141 $result = new self($reader->readInnerXml()); 142 $reader->next(); 143 return $result; 144 145 } 146 147} 148