elementMap = $this->elementMap; return $r; } /** * Returns a fresh xml writer * * @return Writer */ function getWriter() { $w = new Writer(); $w->namespaceMap = $this->namespaceMap; $w->classMap = $this->classMap; return $w; } /** * Parses a document in full. * * Input may be specified as a string or readable stream resource. * The returned value is the value of the root document. * * Specifying the $contextUri allows the parser to figure out what the URI * of the document was. This allows relative URIs within the document to be * expanded easily. * * The $rootElementName is specified by reference and will be populated * with the root element name of the document. * * @param string|resource $input * @param string|null $contextUri * @param string|null $rootElementName * @throws ParseException * @return array|object|string */ function parse($input, $contextUri = null, &$rootElementName = null) { if (is_resource($input)) { // Unfortunately the XMLReader doesn't support streams. When it // does, we can optimize this. $input = stream_get_contents($input); } $r = $this->getReader(); $r->contextUri = $contextUri; $r->xml($input); $result = $r->parse(); $rootElementName = $result['name']; return $result['value']; } /** * Parses a document in full, and specify what the expected root element * name is. * * This function works similar to parse, but the difference is that the * user can specify what the expected name of the root element should be, * in clark notation. * * This is useful in cases where you expected a specific document to be * passed, and reduces the amount of if statements. * * It's also possible to pass an array of expected rootElements if your * code may expect more than one document type. * * @param string|string[] $rootElementName * @param string|resource $input * @param string|null $contextUri * @return void */ function expect($rootElementName, $input, $contextUri = null) { if (is_resource($input)) { // Unfortunately the XMLReader doesn't support streams. When it // does, we can optimize this. $input = stream_get_contents($input); } $r = $this->getReader(); $r->contextUri = $contextUri; $r->xml($input); $rootElementName = (array)$rootElementName; foreach ($rootElementName as &$rEl) { if ($rEl[0] !== '{') $rEl = '{}' . $rEl; } $result = $r->parse(); if (!in_array($result['name'], $rootElementName, true)) { throw new ParseException('Expected ' . implode(' or ', (array)$rootElementName) . ' but received ' . $result['name'] . ' as the root element'); } return $result['value']; } /** * Generates an XML document in one go. * * The $rootElement must be specified in clark notation. * The value must be a string, an array or an object implementing * XmlSerializable. Basically, anything that's supported by the Writer * object. * * $contextUri can be used to specify a sort of 'root' of the PHP application, * in case the xml document is used as a http response. * * This allows an implementor to easily create URI's relative to the root * of the domain. * * @param string $rootElementName * @param string|array|XmlSerializable $value * @param string|null $contextUri */ function write($rootElementName, $value, $contextUri = null) { $w = $this->getWriter(); $w->openMemory(); $w->contextUri = $contextUri; $w->setIndent(true); $w->startDocument(); $w->writeElement($rootElementName, $value); return $w->outputMemory(); } /** * Map an xml element to a PHP class. * * Calling this function will automatically setup the Reader and Writer * classes to turn a specific XML element to a PHP class. * * For example, given a class such as : * * class Author { * public $firstName; * public $lastName; * } * * and an XML element such as: * * * ... * ... * * * These can easily be mapped by calling: * * $service->mapValueObject('{http://example.org}author', 'Author'); * * @param string $elementName * @param object $className * @return void */ function mapValueObject($elementName, $className) { list($namespace) = self::parseClarkNotation($elementName); $this->elementMap[$elementName] = function(Reader $reader) use ($className, $namespace) { return \Sabre\Xml\Deserializer\valueObject($reader, $className, $namespace); }; $this->classMap[$className] = function(Writer $writer, $valueObject) use ($namespace) { return \Sabre\Xml\Serializer\valueObject($writer, $valueObject, $namespace); }; $this->valueObjectMap[$className] = $elementName; } /** * Writes a value object. * * This function largely behaves similar to write(), except that it's * intended specifically to serialize a Value Object into an XML document. * * The ValueObject must have been previously registered using * mapValueObject(). * * @param object $object * @param string $contextUri * @return void */ function writeValueObject($object, $contextUri = null) { if (!isset($this->valueObjectMap[get_class($object)])) { throw new \InvalidArgumentException('"' . get_class($object) . '" is not a registered value object class. Register your class with mapValueObject.'); } return $this->write( $this->valueObjectMap[get_class($object)], $object, $contextUri ); } /** * Parses a clark-notation string, and returns the namespace and element * name components. * * If the string was invalid, it will throw an InvalidArgumentException. * * @param string $str * @throws InvalidArgumentException * @return array */ static function parseClarkNotation($str) { static $cache = []; if (!isset($cache[$str])) { if (!preg_match('/^{([^}]*)}(.*)$/', $str, $matches)) { throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string'); } $cache[$str] = [ $matches[1], $matches[2] ]; } return $cache[$str]; } /** * A list of classes and which XML elements they map to. */ protected $valueObjectMap = []; }