1<?php 2 3namespace Sabre\Xml; 4 5/** 6 * XML parsing and writing service. 7 * 8 * You are encouraged to make a instance of this for your application and 9 * potentially extend it, as a central API point for dealing with xml and 10 * configuring the reader and writer. 11 * 12 * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). 13 * @author Evert Pot (http://evertpot.com/) 14 * @license http://sabre.io/license/ Modified BSD License 15 */ 16class Service { 17 18 /** 19 * This is the element map. It contains a list of XML elements (in clark 20 * notation) as keys and PHP class names as values. 21 * 22 * The PHP class names must implement Sabre\Xml\Element. 23 * 24 * Values may also be a callable. In that case the function will be called 25 * directly. 26 * 27 * @var array 28 */ 29 public $elementMap = []; 30 31 /** 32 * This is a list of namespaces that you want to give default prefixes. 33 * 34 * You must make sure you create this entire list before starting to write. 35 * They should be registered on the root element. 36 * 37 * @var array 38 */ 39 public $namespaceMap = []; 40 41 /** 42 * Returns a fresh XML Reader 43 * 44 * @return Reader 45 */ 46 function getReader() { 47 48 $r = new Reader(); 49 $r->elementMap = $this->elementMap; 50 return $r; 51 52 } 53 54 /** 55 * Returns a fresh xml writer 56 * 57 * @return Writer 58 */ 59 function getWriter() { 60 61 $w = new Writer(); 62 $w->namespaceMap = $this->namespaceMap; 63 return $w; 64 65 } 66 67 /** 68 * Parses a document in full. 69 * 70 * Input may be specified as a string or readable stream resource. 71 * The returned value is the value of the root document. 72 * 73 * Specifying the $contextUri allows the parser to figure out what the URI 74 * of the document was. This allows relative URIs within the document to be 75 * expanded easily. 76 * 77 * The $rootElementName is specified by reference and will be populated 78 * with the root element name of the document. 79 * 80 * @param string|resource $input 81 * @param string|null $contextUri 82 * @param string|null $rootElementName 83 * @throws ParseException 84 * @return array|object|string 85 */ 86 function parse($input, $contextUri = null, &$rootElementName = null) { 87 88 if (is_resource($input)) { 89 // Unfortunately the XMLReader doesn't support streams. When it 90 // does, we can optimize this. 91 $input = stream_get_contents($input); 92 } 93 $r = $this->getReader(); 94 $r->contextUri = $contextUri; 95 $r->xml($input); 96 97 $result = $r->parse(); 98 $rootElementName = $result['name']; 99 return $result['value']; 100 101 } 102 103 /** 104 * Parses a document in full, and specify what the expected root element 105 * name is. 106 * 107 * This function works similar to parse, but the difference is that the 108 * user can specify what the expected name of the root element should be, 109 * in clark notation. 110 * 111 * This is useful in cases where you expected a specific document to be 112 * passed, and reduces the amount of if statements. 113 * 114 * @param string $rootElementName 115 * @param string|resource $input 116 * @param string|null $contextUri 117 * @return void 118 */ 119 function expect($rootElementName, $input, $contextUri = null) { 120 121 if (is_resource($input)) { 122 // Unfortunately the XMLReader doesn't support streams. When it 123 // does, we can optimize this. 124 $input = stream_get_contents($input); 125 } 126 $r = $this->getReader(); 127 $r->contextUri = $contextUri; 128 $r->xml($input); 129 130 $result = $r->parse(); 131 if ($rootElementName !== $result['name']) { 132 throw new ParseException('Expected ' . $rootElementName . ' but received ' . $result['name'] . ' as the root element'); 133 } 134 return $result['value']; 135 136 } 137 138 /** 139 * Generates an XML document in one go. 140 * 141 * The $rootElement must be specified in clark notation. 142 * The value must be a string, an array or an object implementing 143 * XmlSerializable. Basically, anything that's supported by the Writer 144 * object. 145 * 146 * $contextUri can be used to specify a sort of 'root' of the PHP application, 147 * in case the xml document is used as a http response. 148 * 149 * This allows an implementor to easily create URI's relative to the root 150 * of the domain. 151 * 152 * @param string $rootElementName 153 * @param string|array|XmlSerializable $value 154 * @param string|null $contextUri 155 */ 156 function write($rootElementName, $value, $contextUri = null) { 157 158 $w = $this->getWriter(); 159 $w->openMemory(); 160 $w->contextUri = $contextUri; 161 $w->setIndent(true); 162 $w->startDocument(); 163 $w->writeElement($rootElementName, $value); 164 return $w->outputMemory(); 165 166 } 167 168 169 /** 170 * Parses a clark-notation string, and returns the namespace and element 171 * name components. 172 * 173 * If the string was invalid, it will throw an InvalidArgumentException. 174 * 175 * @param string $str 176 * @throws InvalidArgumentException 177 * @return array 178 */ 179 static function parseClarkNotation($str) { 180 181 if (!preg_match('/^{([^}]*)}(.*)$/', $str, $matches)) { 182 throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string'); 183 } 184 185 return [ 186 $matches[1], 187 $matches[2] 188 ]; 189 190 } 191 192 193} 194