pax_global_header00006660000000000000000000000064126602077030014515gustar00rootroot0000000000000052 comment=fb07d78e232ec37f5760f579e15694d150ff0da6 sabre-xml-1.4.0/000077500000000000000000000000001266020770300134115ustar00rootroot00000000000000sabre-xml-1.4.0/.gitignore000066400000000000000000000001531266020770300154000ustar00rootroot00000000000000vendor composer.lock tests/cov .*.swp # Composer binaries bin/phpunit bin/php-cs-fixer bin/sabre-cs-fixer sabre-xml-1.4.0/.travis.yml000066400000000000000000000004061266020770300155220ustar00rootroot00000000000000language: php php: - 5.4 - 5.5 - 5.6 - 7 - hhvm matrix: fast_finish: true sudo: false cache: vendor script: - ./bin/phpunit --configuration tests/phpunit.xml.dist - ./bin/sabre-cs-fixer fix . --dry-run --diff before_script: composer install sabre-xml-1.4.0/CHANGELOG.md000066400000000000000000000141101266020770300152170ustar00rootroot00000000000000ChangeLog ========= 1.4.0 (2016-02-14) ------------------ * Any array thrown into the serializer with numeric keys is now simply traversed and each individual item is serialized. This fixes an issue related to serializing value objects with array children. * When serializing value objects, properties that have a null value or an empty array are now skipped. We believe this to be the saner default, but does constitute a BC break for those depending on this. * Serializing array properties in value objects was broken. 1.3.0 (2015-12-29) ------------------ * The `Service` class adds a new `mapValueObject` method which provides basic capabilities to map between ValueObjects and XML. * #61: You can now specify serializers for specific classes, allowing you separate the object you want to serialize from the serializer. This uses the `$classMap` property which is defined on both the `Service` and `Writer`. * It's now possible to pass an array of possible root elements to `Sabre\Xml\Service::expect()`. * Moved some parsing logic to `Reader::getDeserializerForElementName()`, so people with more advanced use-cases can implement their own logic there. * #63: When serializing elements using arrays, the `value` key in the array is now optional. * #62: Added a `keyValue` deserializer function. This can be used instead of the `Element\KeyValue` class and is a lot more flexible. (@staabm) * Also added an `enum` deserializer function to replace `Element\Elements`. * Using an empty string for a namespace prefix now has the same effect as `null`. 1.2.0 (2015-08-30) ------------------ * #53: Added `parseGetElements`, a function like `parseInnerTree`, except that it always returns an array of elements, or an empty array. 1.1.0 (2015-06-29) ------------------ * #44, #45: Catching broken and invalid XML better and throwing `Sabre\Xml\LibXMLException` whenever we encounter errors. (@stefanmajoor, @DaanBiesterbos) 1.0.0 (2015-05-25) ------------------ * No functional changes since 0.4.3. Marking it as 1.0.0 as a promise for API stability. * Using php-cs-fixer for automated CS enforcement. 0.4.3 (2015-04-01) ----------------- * Minor tweaks for the public release. 0.4.2 (2015-03-20) ------------------ * Removed `constants.php` again. They messed with PHPUnit and don't really provide a great benefit. * #41: Correctly handle self-closing xml elements. 0.4.1 (2015-03-19) ------------------ * #40: An element with an empty namespace (xmlns="") is not allowed to have a prefix. This is now fixed. 0.4.0 (2015-03-18) ------------------ * Added `Sabre\Xml\Service`. This is intended as a simple way to centrally configure xml applications and easily parse/write things from there. #35, #38. * Renamed 'baseUri' to 'contextUri' everywhere. * #36: Added a few convenience constants to `lib/constants.php`. * `Sabre\Xml\Util::parseClarkNotation` is now in the `Sabre\Xml\Service` class. 0.3.1 (2015-02-08) ------------------ * Added `XmlDeserializable` to match `XmlSerializable`. 0.3.0 (2015-02-06) ------------------ * Added `$elementMap` argument to parseInnerTree, for quickly overriding parsing rules within an element. 0.2.2 (2015-02-05) ------------------ * Now depends on sabre/uri 1.0. 0.2.1 (2014-12-17) ------------------ * LibXMLException now inherits from ParseException, so it's easy for users to catch any exception thrown by the parser. 0.2.0 (2014-12-05) ------------------ * Major BC Break: method names for the Element interface have been renamed from `serializeXml` and `deserializeXml` to `xmlSerialize` and `xmlDeserialize`. This is so that it matches PHP's `JsonSerializable` interface. * #25: Added `XmlSerializable` to allow people to write serializers without having to implement a deserializer in the same class. * #26: Renamed the `Sabre\XML` namespace to `Sabre\Xml`. Due to composer magic and the fact that PHP namespace are case-insensitive, this should not affect anyone, unless you are doing exact string matches on class names. * #23: It's not possible to automatically extract or serialize Xml fragments from documents using `Sabre\Xml\Element\XmlFragment`. 0.1.0 (2014-11-24) ------------------ * #16: Added ability to override `elementMap`, `namespaceMap` and `baseUri` for a fragment of a document during reading an writing using `pushContext` and `popContext`. * Removed: `Writer::$context` and `Reader::$context`. * #15: Added `Reader::$baseUri` to match `Writer::$baseUri`. * #20: Allow callbacks to be used instead of `Element` classes in the `Reader`. * #25: Added `readText` to quickly grab all text from a node and advance the reader to the next node. * #15: Added `Sabre\XML\Element\Uri`. 0.0.6 (2014-09-26) ------------------ * Added: `CData` element. * #13: Better support for xml with no namespaces. (@kalmas) * Switched to PSR-4 directory structure. 0.0.5 (2013-03-27) ------------------ * Added: baseUri property to the Writer class. * Added: The writeElement method can now write complex elements. * Added: Throwing exception when invalid objects are written. 0.0.4 (2013-03-14) ------------------ * Fixed: The KeyValue parser was skipping over elements when there was no whitespace between them. * Fixed: Clearing libxml errors after parsing. * Added: Support for CDATA. * Added: Context properties. 0.0.3 (2013-02-22) ------------------ * Changed: Reader::parse returns an array with 1 level less depth. * Added: A LibXMLException is now thrown if the XMLReader comes across an error. * Fixed: Both the Elements and KeyValue parsers had severe issues with nesting. * Fixed: The reader now detects when the end of the document is hit before it should (because we're still parsing an element). 0.0.2 (2013-02-17) ------------------ * Added: Elements parser. * Added: KeyValue parser. * Change: Reader::parseSubTree is now named parseInnerTree, and returns either a string (in case of a text-node), or an array (in case there were child elements). * Added: Reader::parseCurrentElement is now public. 0.0.1 (2013-02-07) ------------------ * First alpha release Project started: 2012-11-13. First experiments in June 2009. sabre-xml-1.4.0/LICENSE000066400000000000000000000030411266020770300144140ustar00rootroot00000000000000Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Sabre nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. sabre-xml-1.4.0/README.md000066400000000000000000000012741266020770300146740ustar00rootroot00000000000000sabre/xml ========= [![Build Status](https://secure.travis-ci.org/fruux/sabre-xml.svg?branch=master)](http://travis-ci.org/fruux/sabre-xml) The sabre/xml library is a specialized XML reader and writer. Documentation ------------- * [Introduction](http://sabre.io/xml/). * [Installation](http://sabre.io/xml/install/). * [Reading XML](http://sabre.io/xml/reading/). * [Writing XML](http://sabre.io/xml/writing/). Support ------- Head over to the [SabreDAV mailing list](http://groups.google.com/group/sabredav-discuss) for any questions. Made at fruux ------------- This library is being developed by [fruux](https://fruux.com/). Drop us a line for commercial services or enterprise support. sabre-xml-1.4.0/bin/000077500000000000000000000000001266020770300141615ustar00rootroot00000000000000sabre-xml-1.4.0/bin/.empty000066400000000000000000000000001266020770300153060ustar00rootroot00000000000000sabre-xml-1.4.0/composer.json000066400000000000000000000024271266020770300161400ustar00rootroot00000000000000{ "name": "sabre/xml", "description" : "sabre/xml is an XML library that you may not hate.", "keywords" : [ "XML", "XMLReader", "XMLWriter", "DOM" ], "homepage" : "https://sabre.io/xml/", "license" : "BSD-3-Clause", "require" : { "php" : ">=5.4.1", "ext-xmlwriter" : "*", "ext-xmlreader" : "*", "ext-dom" : "*", "lib-libxml" : ">=2.6.20", "sabre/uri" : "~1.0" }, "authors" : [ { "name" : "Evert Pot", "email" : "me@evertpot.com", "homepage" : "http://evertpot.com/", "role" : "Developer" }, { "name": "Markus Staab", "email": "markus.staab@redaxo.de", "role" : "Developer" } ], "support" : { "forum" : "https://groups.google.com/group/sabredav-discuss", "source" : "https://github.com/fruux/sabre-xml" }, "autoload" : { "psr-4" : { "Sabre\\Xml\\" : "lib/" }, "files": [ "lib/Deserializer/functions.php", "lib/Serializer/functions.php" ] }, "bin" : [ ], "require-dev": { "sabre/cs": "~0.0.2", "phpunit/phpunit" : "*" }, "config" : { "bin-dir" : "bin/" } } sabre-xml-1.4.0/lib/000077500000000000000000000000001266020770300141575ustar00rootroot00000000000000sabre-xml-1.4.0/lib/ContextStackTrait.php000066400000000000000000000063431266020770300203140ustar00rootroot00000000000000contextStack[] = [ $this->elementMap, $this->contextUri, $this->namespaceMap, $this->classMap ]; } /** * Restore the previous "context". * * @return null */ function popContext() { list( $this->elementMap, $this->contextUri, $this->namespaceMap, $this->classMap ) = array_pop($this->contextStack); } } sabre-xml-1.4.0/lib/Deserializer/000077500000000000000000000000001266020770300166015ustar00rootroot00000000000000sabre-xml-1.4.0/lib/Deserializer/functions.php000066400000000000000000000141501266020770300213230ustar00rootroot00000000000000value" array. * * For example, keyvalue will parse: * * * * value1 * value2 * * * * Into: * * [ * "{http://sabredav.org/ns}elem1" => "value1", * "{http://sabredav.org/ns}elem2" => "value2", * "{http://sabredav.org/ns}elem3" => null, * ]; * * If you specify the 'namespace' argument, the deserializer will remove * the namespaces of the keys that match that namespace. * * For example, if you call keyValue like this: * * keyValue($reader, 'http://sabredav.org/ns') * * it's output will instead be: * * [ * "elem1" => "value1", * "elem2" => "value2", * "elem3" => null, * ]; * * Attributes will be removed from the top-level elements. If elements with * the same name appear twice in the list, only the last one will be kept. * * * @param Reader $reader * @param string $namespace * @return array */ function keyValue(Reader $reader, $namespace = null) { // If there's no children, we don't do anything. if ($reader->isEmptyElement) { $reader->next(); return []; } $values = []; $reader->read(); do { if ($reader->nodeType === Reader::ELEMENT) { if ($namespace !== null && $reader->namespaceURI === $namespace) { $values[$reader->localName] = $reader->parseCurrentElement()['value']; } else { $clark = $reader->getClark(); $values[$clark] = $reader->parseCurrentElement()['value']; } } else { $reader->read(); } } while ($reader->nodeType !== Reader::END_ELEMENT); $reader->read(); return $values; } /** * The 'enum' deserializer parses elements into a simple list * without values or attributes. * * For example, Elements will parse: * * * * * * * content * * * * Into: * * [ * "{http://sabredav.org/ns}elem1", * "{http://sabredav.org/ns}elem2", * "{http://sabredav.org/ns}elem3", * "{http://sabredav.org/ns}elem4", * "{http://sabredav.org/ns}elem5", * ]; * * This is useful for 'enum'-like structures. * * If the $namespace argument is specified, it will strip the namespace * for all elements that match that. * * For example, * * enum($reader, 'http://sabredav.org/ns') * * would return: * * [ * "elem1", * "elem2", * "elem3", * "elem4", * "elem5", * ]; * * @param Reader $reader * @param string $namespace * @return string[] */ function enum(Reader $reader, $namespace = null) { // If there's no children, we don't do anything. if ($reader->isEmptyElement) { $reader->next(); return []; } $reader->read(); $currentDepth = $reader->depth; $values = []; do { if ($reader->nodeType !== Reader::ELEMENT) { continue; } if (!is_null($namespace) && $namespace === $reader->namespaceURI) { $values[] = $reader->localName; } else { $values[] = $reader->getClark(); } } while ($reader->depth >= $currentDepth && $reader->next()); $reader->next(); return $values; } /** * The valueObject deserializer turns an xml element into a PHP object of * a specific class. * * This is primarily used by the mapValueObject function from the Service * class, but it can also easily be used for more specific situations. * * @param Reader $reader * @param string $className * @param string $namespace * @return object */ function valueObject(Reader $reader, $className, $namespace) { $valueObject = new $className(); if ($reader->isEmptyElement) { $reader->next(); return $valueObject; } $defaultProperties = get_class_vars($className); $reader->read(); do { if ($reader->nodeType === Reader::ELEMENT && $reader->namespaceURI == $namespace) { if (property_exists($valueObject, $reader->localName)) { if (is_array($defaultProperties[$reader->localName])) { $valueObject->{$reader->localName}[] = $reader->parseCurrentElement()['value']; } else { $valueObject->{$reader->localName} = $reader->parseCurrentElement()['value']; } } else { // Ignore property $reader->next(); } } else { $reader->read(); } } while ($reader->nodeType !== Reader::END_ELEMENT); $reader->read(); return $valueObject; } /* * This deserializer helps you deserialize xml structures that look like * this: * * * ... * ... * ... * * * Many XML documents use patterns like that, and this deserializer * allow you to get all the 'items' as an array. * * In that previous example, you would register the deserializer as such: * * $reader->elementMap['{}collection'] = function($reader) { * return repeatingElements($reader, '{}item'); * } * * The repeatingElements deserializer simply returns everything as an array. * * @param Reader $reader * @param string $childElementName Element name in clark-notation * @return array */ function repeatingElements(Reader $reader, $childElementName) { $result = []; foreach ($reader->parseGetElements() as $element) { if ($element['name'] === $childElementName) { $result[] = $element['value']; } } return $result; } sabre-xml-1.4.0/lib/Element.php000066400000000000000000000010141266020770300162550ustar00rootroot00000000000000value = $value; } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializable should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Xml\Writer $writer) { $writer->write($this->value); } /** * The deserialize method is called during xml parsing. * * This method is called statictly, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are * free to return other data as well. * * Important note 2: You are responsible for advancing the reader to the * next element. Not doing anything will result in a never-ending loop. * * If you just want to skip parsing for this element altogether, you can * just call $reader->next(); * * $reader->parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * * @param Xml\Reader $reader * @return mixed */ static function xmlDeserialize(Xml\Reader $reader) { $subTree = $reader->parseInnerTree(); return $subTree; } } sabre-xml-1.4.0/lib/Element/Cdata.php000066400000000000000000000027611266020770300173030ustar00rootroot00000000000000value = $value; } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Xml\Writer $writer) { $writer->writeCData($this->value); } } sabre-xml-1.4.0/lib/Element/Elements.php000066400000000000000000000053201266020770300200350ustar00rootroot00000000000000 * * * * * content * * * * Into: * * [ * "{http://sabredav.org/ns}elem1", * "{http://sabredav.org/ns}elem2", * "{http://sabredav.org/ns}elem3", * "{http://sabredav.org/ns}elem4", * "{http://sabredav.org/ns}elem5", * ]; * * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class Elements implements Xml\Element { /** * Value to serialize * * @var array */ protected $value; /** * Constructor * * @param array $value */ function __construct(array $value = []) { $this->value = $value; } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Xml\Writer $writer) { Serializer\enum($writer, $this->value); } /** * The deserialize method is called during xml parsing. * * This method is called statictly, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are * free to return other data as well. * * Important note 2: You are responsible for advancing the reader to the * next element. Not doing anything will result in a never-ending loop. * * If you just want to skip parsing for this element altogether, you can * just call $reader->next(); * * $reader->parseSubTree() will parse the entire sub-tree, and advance to * the next element. * * @param Xml\Reader $reader * @return mixed */ static function xmlDeserialize(Xml\Reader $reader) { return Deserializer\enum($reader); } } sabre-xml-1.4.0/lib/Element/KeyValue.php000066400000000000000000000054371266020770300200170ustar00rootroot00000000000000value struct. * * Attributes will be removed, and duplicate child elements are discarded. * Complex values within the elements will be parsed by the 'standard' parser. * * For example, KeyValue will parse: * * * * value1 * value2 * * * * Into: * * [ * "{http://sabredav.org/ns}elem1" => "value1", * "{http://sabredav.org/ns}elem2" => "value2", * "{http://sabredav.org/ns}elem3" => null, * ]; * * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class KeyValue implements Xml\Element { /** * Value to serialize * * @var array */ protected $value; /** * Constructor * * @param array $value */ function __construct(array $value = []) { $this->value = $value; } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Xml\Writer $writer) { $writer->write($this->value); } /** * The deserialize method is called during xml parsing. * * This method is called staticly, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are * free to return other data as well. * * Important note 2: You are responsible for advancing the reader to the * next element. Not doing anything will result in a never-ending loop. * * If you just want to skip parsing for this element altogether, you can * just call $reader->next(); * * $reader->parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * * @param Xml\Reader $reader * @return mixed */ static function xmlDeserialize(Xml\Reader $reader) { return Deserializer\keyValue($reader); } } sabre-xml-1.4.0/lib/Element/Uri.php000066400000000000000000000052641266020770300170270ustar00rootroot00000000000000/foo/bar * http://example.org/hi * * If the uri is relative, it will be automatically expanded to an absolute * url during writing and reading, if the contextUri property is set on the * reader and/or writer. * * @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/). * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class Uri implements Xml\Element { /** * Uri element value. * * @var string */ protected $value; /** * Constructor * * @param string $value */ function __construct($value) { $this->value = $value; } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Xml\Writer $writer) { $writer->text( \Sabre\Uri\resolve( $writer->contextUri, $this->value ) ); } /** * This method is called during xml parsing. * * This method is called statically, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are * free to return other data as well. * * Important note 2: You are responsible for advancing the reader to the * next element. Not doing anything will result in a never-ending loop. * * If you just want to skip parsing for this element altogether, you can * just call $reader->next(); * * $reader->parseSubTree() will parse the entire sub-tree, and advance to * the next element. * * @param Xml\Reader $reader * @return mixed */ static function xmlDeserialize(Xml\Reader $reader) { return new self( \Sabre\Uri\resolve( $reader->contextUri, $reader->readText() ) ); } } sabre-xml-1.4.0/lib/Element/XmlFragment.php000066400000000000000000000101361266020770300205060ustar00rootroot00000000000000xml = $xml; } function getXml() { return $this->xml; } /** * The xmlSerialize metod is called during xml writing. * * Use the $writer argument to write its own xml serialization. * * An important note: do _not_ create a parent element. Any element * implementing XmlSerializble should only ever write what's considered * its 'inner xml'. * * The parent of the current element is responsible for writing a * containing element. * * This allows serializers to be re-used for different element names. * * If you are opening new elements, you must also close them again. * * @param Writer $writer * @return void */ function xmlSerialize(Writer $writer) { $reader = new Reader(); // Wrapping the xml in a container, so root-less values can still be // parsed. $xml = << {$this->getXml()} XML; $reader->xml($xml); while ($reader->read()) { if ($reader->depth < 1) { // Skipping the root node. continue; } switch ($reader->nodeType) { case Reader::ELEMENT : $writer->startElement( $reader->getClark() ); $empty = $reader->isEmptyElement; while ($reader->moveToNextAttribute()) { switch ($reader->namespaceURI) { case '' : $writer->writeAttribute($reader->localName, $reader->value); break; case 'http://www.w3.org/2000/xmlns/' : // Skip namespace declarations break; default : $writer->writeAttribute($reader->getClark(), $reader->value); break; } } if ($empty) { $writer->endElement(); } break; case Reader::CDATA : case Reader::TEXT : $writer->text( $reader->value ); break; case Reader::END_ELEMENT : $writer->endElement(); break; } } } /** * The deserialize method is called during xml parsing. * * This method is called statictly, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are * free to return other data as well. * * You are responsible for advancing the reader to the next element. Not * doing anything will result in a never-ending loop. * * If you just want to skip parsing for this element altogether, you can * just call $reader->next(); * * $reader->parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * * @param Reader $reader * @return mixed */ static function xmlDeserialize(Reader $reader) { $result = new self($reader->readInnerXml()); $reader->next(); return $result; } } sabre-xml-1.4.0/lib/LibXMLException.php000066400000000000000000000022351266020770300176400ustar00rootroot00000000000000errors = $errors; parent::__construct($errors[0]->message . ' on line ' . $errors[0]->line . ', column ' . $errors[0]->column, $code, $previousException); } /** * Returns the LibXML errors * * @return void */ function getErrors() { return $this->errors; } } sabre-xml-1.4.0/lib/ParseException.php000066400000000000000000000005411266020770300176210ustar00rootroot00000000000000localName) { return null; } return '{' . $this->namespaceURI . '}' . $this->localName; } /** * Reads the entire document. * * This function returns an array with the following three elements: * * name - The root element name. * * value - The value for the root element. * * attributes - An array of attributes. * * This function will also disable the standard libxml error handler (which * usually just results in PHP errors), and throw exceptions instead. * * @return array */ function parse() { $previousEntityState = libxml_disable_entity_loader(true); $previousSetting = libxml_use_internal_errors(true); // Really sorry about the silence operator, seems like I have no // choice. See: // // https://bugs.php.net/bug.php?id=64230 while ($this->nodeType !== self::ELEMENT && @$this->read()) { // noop } $result = $this->parseCurrentElement(); $errors = libxml_get_errors(); libxml_clear_errors(); libxml_use_internal_errors($previousSetting); libxml_disable_entity_loader($previousEntityState); if ($errors) { throw new LibXMLException($errors); } return $result; } /** * parseGetElements parses everything in the current sub-tree, * and returns a an array of elements. * * Each element has a 'name', 'value' and 'attributes' key. * * If the the element didn't contain sub-elements, an empty array is always * returned. If there was any text inside the element, it will be * discarded. * * If the $elementMap argument is specified, the existing elementMap will * be overridden while parsing the tree, and restored after this process. * * @param array $elementMap * @return array */ function parseGetElements(array $elementMap = null) { $result = $this->parseInnerTree($elementMap); if (!is_array($result)) { return []; } return $result; } /** * Parses all elements below the current element. * * This method will return a string if this was a text-node, or an array if * there were sub-elements. * * If there's both text and sub-elements, the text will be discarded. * * If the $elementMap argument is specified, the existing elementMap will * be overridden while parsing the tree, and restored after this process. * * @param array $elementMap * @return array|string */ function parseInnerTree(array $elementMap = null) { $text = null; $elements = []; if ($this->nodeType === self::ELEMENT && $this->isEmptyElement) { // Easy! $this->next(); return null; } if (!is_null($elementMap)) { $this->pushContext(); $this->elementMap = $elementMap; } // Really sorry about the silence operator, seems like I have no // choice. See: // // https://bugs.php.net/bug.php?id=64230 if (!@$this->read()) return false; while (true) { if (!$this->isValid()) { $errors = libxml_get_errors(); if ($errors) { libxml_clear_errors(); throw new LibXMLException($errors); } } switch ($this->nodeType) { case self::ELEMENT : $elements[] = $this->parseCurrentElement(); break; case self::TEXT : case self::CDATA : $text .= $this->value; $this->read(); break; case self::END_ELEMENT : // Ensuring we are moving the cursor after the end element. $this->read(); break 2; case self::NONE : throw new ParseException('We hit the end of the document prematurely. This likely means that some parser "eats" too many elements. Do not attempt to continue parsing.'); default : // Advance to the next element $this->read(); break; } } if (!is_null($elementMap)) { $this->popContext(); } return ($elements ? $elements : $text); } /** * Reads all text below the current element, and returns this as a string. * * @return string */ function readText() { $result = ''; $previousDepth = $this->depth; while ($this->read() && $this->depth != $previousDepth) { if (in_array($this->nodeType, [XMLReader::TEXT, XMLReader::CDATA, XMLReader::WHITESPACE])) { $result .= $this->value; } } return $result; } /** * Parses the current XML element. * * This method returns arn array with 3 properties: * * name - A clark-notation XML element name. * * value - The parsed value. * * attributes - A key-value list of attributes. * * @return array */ function parseCurrentElement() { $name = $this->getClark(); $attributes = []; if ($this->hasAttributes) { $attributes = $this->parseAttributes(); } $value = call_user_func( $this->getDeserializerForElementName($name), $this ); return [ 'name' => $name, 'value' => $value, 'attributes' => $attributes, ]; } /** * Grabs all the attributes from the current element, and returns them as a * key-value array. * * If the attributes are part of the same namespace, they will simply be * short keys. If they are defined on a different namespace, the attribute * name will be retured in clark-notation. * * @return array */ function parseAttributes() { $attributes = []; while ($this->moveToNextAttribute()) { if ($this->namespaceURI) { // Ignoring 'xmlns', it doesn't make any sense. if ($this->namespaceURI === 'http://www.w3.org/2000/xmlns/') { continue; } $name = $this->getClark(); $attributes[$name] = $this->value; } else { $attributes[$this->localName] = $this->value; } } $this->moveToElement(); return $attributes; } /** * Returns the function that should be used to parse the element identified * by it's clark-notation name. * * @param string $name * @return callable */ function getDeserializerForElementName($name) { if (!array_key_exists($name, $this->elementMap)) { return ['Sabre\\Xml\\Element\\Base', 'xmlDeserialize']; } $deserializer = $this->elementMap[$name]; if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) { return [ $deserializer, 'xmlDeserialize' ]; } if (is_callable($deserializer)) { return $deserializer; } $type = gettype($deserializer); if ($type === 'string') { $type .= ' (' . $deserializer . ')'; } elseif ($type === 'object') { $type .= ' (' . get_class($deserializer) . ')'; } throw new \LogicException('Could not use this type as a deserializer: ' . $type . ' for element: ' . $name); } } sabre-xml-1.4.0/lib/Serializer/000077500000000000000000000000001266020770300162705ustar00rootroot00000000000000sabre-xml-1.4.0/lib/Serializer/functions.php000066400000000000000000000160151266020770300210140ustar00rootroot00000000000000 * * * content * * * @param Writer $writer * @param string[] $values * @return void */ function enum(Writer $writer, array $values) { foreach ($values as $value) { $writer->writeElement($value); } } /** * The valueObject serializer turns a simple PHP object into a classname. * * Every public property will be encoded as an xml element with the same * name, in the XML namespace as specified. * * Values that are set to null or an empty array are not serialized. To * serialize empty properties, you must specify them as an empty string. * * @param Writer $writer * @param object $valueObject * @param string $namespace */ function valueObject(Writer $writer, $valueObject, $namespace) { foreach (get_object_vars($valueObject) as $key => $val) { if (is_array($val)) { // If $val is an array, it has a special meaning. We need to // generate one child element for each item in $val foreach ($val as $child) { $writer->writeElement('{' . $namespace . '}' . $key, $child); } } elseif ($val !== null) { $writer->writeElement('{' . $namespace . '}' . $key, $val); } } } /** * This serializer helps you serialize xml structures that look like * this: * * * ... * ... * ... * * * In that previous example, this serializer just serializes the item element, * and this could be called like this: * * repeatingElements($writer, $items, '{}item'); * * @param Writer $writer * @param array $items A list of items sabre/xml can serialize. * @param string $childElementName Element name in clark-notation * @return void */ function repeatingElements(Writer $writer, array $items, $childElementName) { foreach ($items as $item) { $writer->writeElement($childElementName, $item); } } /** * This function is the 'default' serializer that is able to serialize most * things, and delegates to other serializers if needed. * * The standardSerializer supports a wide-array of values. * * $value may be a string or integer, it will just write out the string as text. * $value may be an instance of XmlSerializable or Element, in which case it * calls it's xmlSerialize() method. * $value may be a PHP callback/function/closure, in case we call the callback * and give it the Writer as an argument. * $value may be a an object, and if it's in the classMap we automatically call * the correct serializer for it. * $value may be null, in which case we do nothing. * * If $value is an array, the array must look like this: * * [ * [ * 'name' => '{namespaceUri}element-name', * 'value' => '...', * 'attributes' => [ 'attName' => 'attValue' ] * ] * [, * 'name' => '{namespaceUri}element-name2', * 'value' => '...', * ] * ] * * This would result in xml like: * * * ... * * * ... * * * The value property may be any value standardSerializer supports, so you can * nest data-structures this way. Both value and attributes are optional. * * Alternatively, you can also specify the array using this syntax: * * [ * [ * '{namespaceUri}element-name' => '...', * '{namespaceUri}element-name2' => '...', * ] * ] * * This is excellent for simple key->value structures, and here you can also * specify anything for the value. * * You can even mix the two array syntaxes. * * @param Writer $writer * @param string|int|float|bool|array|object * @return void */ function standardSerializer(Writer $writer, $value) { if (is_scalar($value)) { // String, integer, float, boolean $writer->text($value); } elseif ($value instanceof XmlSerializable) { // XmlSerializable classes or Element classes. $value->xmlSerialize($writer); } elseif (is_object($value) && isset($writer->classMap[get_class($value)])) { // It's an object which class appears in the classmap. $writer->classMap[get_class($value)]($writer, $value); } elseif (is_callable($value)) { // A callback $value($writer); } elseif (is_null($value)) { // nothing! } elseif (is_array($value) && array_key_exists('name', $value)) { // if the array had a 'name' element, we assume that this array // describes a 'name' and optionally 'attributes' and 'value'. $name = $value['name']; $attributes = isset($value['attributes']) ? $value['attributes'] : []; $value = isset($value['value']) ? $value['value'] : null; $writer->startElement($name); $writer->writeAttributes($attributes); $writer->write($value); $writer->endElement(); } elseif (is_array($value)) { foreach ($value as $name => $item) { if (is_int($name)) { // This item has a numeric index. We just loop through the // array and throw it back in the writer. standardSerializer($writer, $item); } elseif (is_string($name) && is_array($item) && isset($item['attributes'])) { // The key is used for a name, but $item has 'attributes' and // possibly 'value' $writer->startElement($name); $writer->writeAttributes($item['attributes']); if (isset($item['value'])) { $writer->write($item['value']); } $writer->endElement(); } elseif (is_string($name)) { // This was a plain key-value array. $writer->startElement($name); $writer->write($item); $writer->endElement(); } else { throw new InvalidArgumentException('The writer does not know how to serialize arrays with keys of type: ' . gettype($name)); } } } elseif (is_object($value)) { throw new InvalidArgumentException('The writer cannot serialize objects of class: ' . get_class($value)); } else { throw new InvalidArgumentException('The writer cannot serialize values of type: ' . gettype($value)); } } sabre-xml-1.4.0/lib/Service.php000066400000000000000000000212231266020770300162700ustar00rootroot00000000000000elementMap = $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); $result = $r->parse(); if (!in_array($result['name'], (array)$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) { if (!preg_match('/^{([^}]*)}(.*)$/', $str, $matches)) { throw new \InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string'); } return [ $matches[1], $matches[2] ]; } /** * A list of classes and which XML elements they map to. */ protected $valueObjectMap = []; } sabre-xml-1.4.0/lib/Version.php000066400000000000000000000005301266020770300163130ustar00rootroot00000000000000 "..", * "{namespace}name2" => "..", * ] * * One element will be created for each key in this array. The values of * this array support any format this method supports (this method is * called recursively). * * Array format 2: * * [ * [ * "name" => "{namespace}name1" * "value" => "..", * "attributes" => [ * "attr" => "attribute value", * ] * ], * [ * "name" => "{namespace}name1" * "value" => "..", * "attributes" => [ * "attr" => "attribute value", * ] * ] * ] * * @param mixed $value * @return void */ function write($value) { Serializer\standardSerializer($this, $value); } /** * Opens a new element. * * You can either just use a local elementname, or you can use clark- * notation to start a new element. * * Example: * * $writer->startElement('{http://www.w3.org/2005/Atom}entry'); * * Would result in something like: * * * * @param string $name * @return bool */ function startElement($name) { if ($name[0] === '{') { list($namespace, $localName) = Service::parseClarkNotation($name); if (array_key_exists($namespace, $this->namespaceMap)) { $result = $this->startElementNS( $this->namespaceMap[$namespace] === '' ? null : $this->namespaceMap[$namespace], $localName, null ); } else { // An empty namespace means it's the global namespace. This is // allowed, but it mustn't get a prefix. if ($namespace === "" || $namespace === null) { $result = $this->startElement($localName); $this->writeAttribute('xmlns', ''); } else { if (!isset($this->adhocNamespaces[$namespace])) { $this->adhocNamespaces[$namespace] = 'x' . (count($this->adhocNamespaces) + 1); } $result = $this->startElementNS($this->adhocNamespaces[$namespace], $localName, $namespace); } } } else { $result = parent::startElement($name); } if (!$this->namespacesWritten) { foreach ($this->namespaceMap as $namespace => $prefix) { $this->writeAttribute(($prefix ? 'xmlns:' . $prefix : 'xmlns'), $namespace); } $this->namespacesWritten = true; } return $result; } /** * Write a full element tag and it's contents. * * This method automatically closes the element as well. * * The element name may be specified in clark-notation. * * Examples: * * $writer->writeElement('{http://www.w3.org/2005/Atom}author',null); * becomes: * * * $writer->writeElement('{http://www.w3.org/2005/Atom}author', [ * '{http://www.w3.org/2005/Atom}name' => 'Evert Pot', * ]); * becomes: * Evert Pot * * @param string $name * @param string $content * @return bool */ function writeElement($name, $content = null) { $this->startElement($name); if (!is_null($content)) { $this->write($content); } $this->endElement(); } /** * Writes a list of attributes. * * Attributes are specified as a key->value array. * * The key is an attribute name. If the key is a 'localName', the current * xml namespace is assumed. If it's a 'clark notation key', this namespace * will be used instead. * * @param array $attributes * @return void */ function writeAttributes(array $attributes) { foreach ($attributes as $name => $value) { $this->writeAttribute($name, $value); } } /** * Writes a new attribute. * * The name may be specified in clark-notation. * * Returns true when successful. * * @param string $name * @param string $value * @return bool */ function writeAttribute($name, $value) { if ($name[0] === '{') { list( $namespace, $localName ) = Service::parseClarkNotation($name); if (array_key_exists($namespace, $this->namespaceMap)) { // It's an attribute with a namespace we know $this->writeAttribute( $this->namespaceMap[$namespace] . ':' . $localName, $value ); } else { // We don't know the namespace, we must add it in-line if (!isset($this->adhocNamespaces[$namespace])) { $this->adhocNamespaces[$namespace] = 'x' . (count($this->adhocNamespaces) + 1); } $this->writeAttributeNS( $this->adhocNamespaces[$namespace], $localName, $namespace, $value ); } } else { return parent::writeAttribute($name, $value); } } } sabre-xml-1.4.0/lib/XmlDeserializable.php000066400000000000000000000022601266020770300202700ustar00rootroot00000000000000next(); * * $reader->parseInnerTree() will parse the entire sub-tree, and advance to * the next element. * * @param Reader $reader * @return mixed */ static function xmlDeserialize(Reader $reader); } sabre-xml-1.4.0/lib/XmlSerializable.php000066400000000000000000000020031266020770300177520ustar00rootroot00000000000000stack = $this->getMockForTrait('Sabre\\Xml\\ContextStackTrait'); } function testPushAndPull() { $this->stack->contextUri = '/foo/bar'; $this->stack->elementMap['{DAV:}foo'] = 'Bar'; $this->stack->namespaceMap['DAV:'] = 'd'; $this->stack->pushContext(); $this->assertEquals('/foo/bar', $this->stack->contextUri); $this->assertEquals('Bar', $this->stack->elementMap['{DAV:}foo']); $this->assertEquals('d', $this->stack->namespaceMap['DAV:']); $this->stack->contextUri = '/gir/zim'; $this->stack->elementMap['{DAV:}foo'] = 'newBar'; $this->stack->namespaceMap['DAV:'] = 'dd'; $this->stack->popContext(); $this->assertEquals('/foo/bar', $this->stack->contextUri); $this->assertEquals('Bar', $this->stack->elementMap['{DAV:}foo']); $this->assertEquals('d', $this->stack->namespaceMap['DAV:']); } } sabre-xml-1.4.0/tests/Sabre/Xml/Deserializer/000077500000000000000000000000001266020770300207715ustar00rootroot00000000000000sabre-xml-1.4.0/tests/Sabre/Xml/Deserializer/EnumTest.php000066400000000000000000000020431266020770300232450ustar00rootroot00000000000000elementMap['{urn:test}root'] = 'Sabre\Xml\Deserializer\enum'; $xml = << XML; $result = $service->parse($xml); $expected = [ '{urn:test}foo1', '{urn:test}foo2', ]; $this->assertEquals($expected, $result); } function testDeserializeDefaultNamespace() { $service = new Service(); $service->elementMap['{urn:test}root'] = function($reader) { return enum($reader, 'urn:test'); }; $xml = << XML; $result = $service->parse($xml); $expected = [ 'foo1', 'foo2', ]; $this->assertEquals($expected, $result); } } sabre-xml-1.4.0/tests/Sabre/Xml/Deserializer/KeyValueTest.php000077500000000000000000000035361266020770300241010ustar00rootroot00000000000000 hi foo foo & bar BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}struct' => function(Reader $reader) { return keyValue($reader, 'http://sabredav.org/ns'); } ]; $reader->xml($input); $output = $reader->parse(); $this->assertEquals([ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}struct', 'value' => [ 'elem1' => null, 'elem2' => 'hi', '{http://sabredav.org/another-ns}elem3' => [ [ 'name' => '{http://sabredav.org/another-ns}elem4', 'value' => 'foo', 'attributes' => [], ], [ 'name' => '{http://sabredav.org/another-ns}elem5', 'value' => 'foo & bar', 'attributes' => [], ], ] ], 'attributes' => [], ] ], 'attributes' => [], ], $output); } } sabre-xml-1.4.0/tests/Sabre/Xml/Deserializer/RepeatingElementsTest.php000066400000000000000000000012171266020770300257560ustar00rootroot00000000000000elementMap['{urn:test}collection'] = function($reader) { return repeatingElements($reader, '{urn:test}item'); }; $xml = << foo bar XML; $result = $service->parse($xml); $expected = [ 'foo', 'bar', ]; $this->assertEquals($expected, $result); } } sabre-xml-1.4.0/tests/Sabre/Xml/Deserializer/ValueObjectTest.php000066400000000000000000000070501266020770300245470ustar00rootroot00000000000000 Harry Turtle XML; $reader = new Reader(); $reader->xml($input); $reader->elementMap = [ '{urn:foo}foo' => function(Reader $reader) { return valueObject($reader, 'Sabre\\Xml\\Deserializer\\TestVo', 'urn:foo'); } ]; $output = $reader->parse(); $vo = new TestVo(); $vo->firstName = 'Harry'; $vo->lastName = 'Turtle'; $expected = [ 'name' => '{urn:foo}foo', 'value' => $vo, 'attributes' => [] ]; $this->assertEquals( $expected, $output ); } function testDeserializeValueObjectIgnoredElement() { $input = << Harry Turtle harry@example.org XML; $reader = new Reader(); $reader->xml($input); $reader->elementMap = [ '{urn:foo}foo' => function(Reader $reader) { return valueObject($reader, 'Sabre\\Xml\\Deserializer\\TestVo', 'urn:foo'); } ]; $output = $reader->parse(); $vo = new TestVo(); $vo->firstName = 'Harry'; $vo->lastName = 'Turtle'; $expected = [ 'name' => '{urn:foo}foo', 'value' => $vo, 'attributes' => [] ]; $this->assertEquals( $expected, $output ); } function testDeserializeValueObjectAutoArray() { $input = << Harry Turtle http://example.org/ http://example.net/ XML; $reader = new Reader(); $reader->xml($input); $reader->elementMap = [ '{urn:foo}foo' => function(Reader $reader) { return valueObject($reader, 'Sabre\\Xml\\Deserializer\\TestVo', 'urn:foo'); } ]; $output = $reader->parse(); $vo = new TestVo(); $vo->firstName = 'Harry'; $vo->lastName = 'Turtle'; $vo->link = [ 'http://example.org/', 'http://example.net/', ]; $expected = [ 'name' => '{urn:foo}foo', 'value' => $vo, 'attributes' => [] ]; $this->assertEquals( $expected, $output ); } function testDeserializeValueObjectEmpty() { $input = << XML; $reader = new Reader(); $reader->xml($input); $reader->elementMap = [ '{urn:foo}foo' => function(Reader $reader) { return valueObject($reader, 'Sabre\\Xml\\Deserializer\\TestVo', 'urn:foo'); } ]; $output = $reader->parse(); $vo = new TestVo(); $expected = [ 'name' => '{urn:foo}foo', 'value' => $vo, 'attributes' => [] ]; $this->assertEquals( $expected, $output ); } } class TestVo { public $firstName; public $lastName; public $link = []; } sabre-xml-1.4.0/tests/Sabre/Xml/Element/000077500000000000000000000000001266020770300177405ustar00rootroot00000000000000sabre-xml-1.4.0/tests/Sabre/Xml/Element/CDataTest.php000066400000000000000000000022071266020770300222660ustar00rootroot00000000000000 BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}blabla' => 'Sabre\\Xml\\Element\\Cdata', ]; $reader->xml($input); $output = $reader->parse(); } function testSerialize() { $writer = new Writer(); $writer->namespaceMap = [ 'http://sabredav.org/ns' => null ]; $writer->openMemory(); $writer->startDocument('1.0'); $writer->setIndent(true); $writer->write([ '{http://sabredav.org/ns}root' => new Cdata(''), ]); $output = $writer->outputMemory(); $expected = << ]]> XML; $this->assertEquals($expected, $output); } } sabre-xml-1.4.0/tests/Sabre/Xml/Element/Eater.php000066400000000000000000000042601266020770300215130ustar00rootroot00000000000000startElement('{http://sabredav.org/ns}elem1'); $writer->write('hiiii!'); $writer->endElement(); } /** * The deserialize method is called during xml parsing. * * This method is called statictly, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are * free to return other data as well. * * Important note 2: You are responsible for advancing the reader to the * next element. Not doing anything will result in a never-ending loop. * * If you just want to skip parsing for this element altogether, you can * just call $reader->next(); * * $reader->parseSubTree() will parse the entire sub-tree, and advance to * the next element. * * @param Xml\Reader $reader * @return mixed */ static function xmlDeserialize(Xml\Reader $reader) { $reader->next(); $count = 1; while ($count) { $reader->read(); if ($reader->nodeType === $reader::END_ELEMENT) { $count--; } } $reader->read(); } } sabre-xml-1.4.0/tests/Sabre/Xml/Element/ElementsTest.php000066400000000000000000000066341266020770300230760ustar00rootroot00000000000000 content BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}listThingy' => 'Sabre\\Xml\\Element\\Elements', ]; $reader->xml($input); $output = $reader->parse(); $this->assertEquals([ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}listThingy', 'value' => [ '{http://sabredav.org/ns}elem1', '{http://sabredav.org/ns}elem2', '{http://sabredav.org/ns}elem3', '{http://sabredav.org/ns}elem4', '{http://sabredav.org/ns}elem5', '{http://sabredav.org/ns}elem6', ], 'attributes' => [], ], [ 'name' => '{http://sabredav.org/ns}listThingy', 'value' => [], 'attributes' => [], ], [ 'name' => '{http://sabredav.org/ns}otherThing', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => null, 'attributes' => [], ], [ 'name' => '{http://sabredav.org/ns}elem2', 'value' => null, 'attributes' => [], ], [ 'name' => '{http://sabredav.org/ns}elem3', 'value' => null, 'attributes' => [], ], ], 'attributes' => [], ], ], 'attributes' => [], ], $output); } function testSerialize() { $value = [ '{http://sabredav.org/ns}elem1', '{http://sabredav.org/ns}elem2', '{http://sabredav.org/ns}elem3', '{http://sabredav.org/ns}elem4', '{http://sabredav.org/ns}elem5', '{http://sabredav.org/ns}elem6', ]; $writer = new Writer(); $writer->namespaceMap = [ 'http://sabredav.org/ns' => null ]; $writer->openMemory(); $writer->startDocument('1.0'); $writer->setIndent(true); $writer->write([ '{http://sabredav.org/ns}root' => new Elements($value), ]); $output = $writer->outputMemory(); $expected = << XML; $this->assertEquals($expected, $output); } } sabre-xml-1.4.0/tests/Sabre/Xml/Element/KeyValueTest.php000066400000000000000000000133271266020770300230440ustar00rootroot00000000000000 hi foo foo & bar Hithere BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}struct' => 'Sabre\\Xml\\Element\\KeyValue', ]; $reader->xml($input); $output = $reader->parse(); $this->assertEquals([ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}struct', 'value' => [ '{http://sabredav.org/ns}elem1' => null, '{http://sabredav.org/ns}elem2' => 'hi', '{http://sabredav.org/ns}elem3' => [ [ 'name' => '{http://sabredav.org/ns}elem4', 'value' => 'foo', 'attributes' => [], ], [ 'name' => '{http://sabredav.org/ns}elem5', 'value' => 'foo & bar', 'attributes' => [], ], ], '{http://sabredav.org/ns}elem6' => 'Hithere', ], 'attributes' => [], ], [ 'name' => '{http://sabredav.org/ns}struct', 'value' => [], 'attributes' => [], ], [ 'name' => '{http://sabredav.org/ns}otherThing', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => null, 'attributes' => [], ], ], 'attributes' => [], ], ], 'attributes' => [], ], $output); } /** * This test was added to find out why an element gets eaten by the * SabreDAV MKCOL parser. */ function testElementEater() { $input = << bla BLA; $reader = new Reader(); $reader->elementMap = [ '{DAV:}set' => 'Sabre\\Xml\\Element\\KeyValue', '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', '{DAV:}resourcetype' => 'Sabre\\Xml\\Element\\Elements', ]; $reader->xml($input); $expected = [ 'name' => '{DAV:}mkcol', 'value' => [ [ 'name' => '{DAV:}set', 'value' => [ '{DAV:}prop' => [ '{DAV:}resourcetype' => [ '{DAV:}collection', ], '{DAV:}displayname' => 'bla', ], ], 'attributes' => [], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $reader->parse()); } function testSerialize() { $value = [ '{http://sabredav.org/ns}elem1' => null, '{http://sabredav.org/ns}elem2' => 'textValue', '{http://sabredav.org/ns}elem3' => [ '{http://sabredav.org/ns}elem4' => 'text2', '{http://sabredav.org/ns}elem5' => null, ], '{http://sabredav.org/ns}elem6' => 'text3', ]; $writer = new Writer(); $writer->namespaceMap = [ 'http://sabredav.org/ns' => null ]; $writer->openMemory(); $writer->startDocument('1.0'); $writer->setIndent(true); $writer->write([ '{http://sabredav.org/ns}root' => new KeyValue($value), ]); $output = $writer->outputMemory(); $expected = << textValue text2 text3 XML; $this->assertEquals($expected, $output); } /** * I discovered that when there's no whitespace between elements, elements * can get skipped. */ function testElementSkipProblem() { $input = << val3val4val5 BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}root' => 'Sabre\\Xml\\Element\\KeyValue', ]; $reader->xml($input); $output = $reader->parse(); $this->assertEquals([ 'name' => '{http://sabredav.org/ns}root', 'value' => [ '{http://sabredav.org/ns}elem3' => 'val3', '{http://sabredav.org/ns}elem4' => 'val4', '{http://sabredav.org/ns}elem5' => 'val5', ], 'attributes' => [], ], $output); } } sabre-xml-1.4.0/tests/Sabre/Xml/Element/Mock.php000066400000000000000000000033111266020770300213400ustar00rootroot00000000000000startElement('{http://sabredav.org/ns}elem1'); $writer->write('hiiii!'); $writer->endElement(); } /** * The deserialize method is called during xml parsing. * * This method is called statictly, this is because in theory this method * may be used as a type of constructor, or factory method. * * Often you want to return an instance of the current class, but you are * free to return other data as well. * * Important note 2: You are responsible for advancing the reader to the * next element. Not doing anything will result in a never-ending loop. * * If you just want to skip parsing for this element altogether, you can * just call $reader->next(); * * $reader->parseSubTree() will parse the entire sub-tree, and advance to * the next element. * * @param Xml\Reader $reader * @return mixed */ static function xmlDeserialize(Xml\Reader $reader) { $reader->next(); return 'foobar'; } } sabre-xml-1.4.0/tests/Sabre/Xml/Element/UriTest.php000066400000000000000000000033421266020770300220520ustar00rootroot00000000000000 /foo/bar BLA; $reader = new Reader(); $reader->contextUri = 'http://example.org/'; $reader->elementMap = [ '{http://sabredav.org/ns}uri' => 'Sabre\\Xml\\Element\\Uri', ]; $reader->xml($input); $output = $reader->parse(); $this->assertEquals( [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}uri', 'value' => new Uri('http://example.org/foo/bar'), 'attributes' => [], ] ], 'attributes' => [], ], $output ); } function testSerialize() { $writer = new Writer(); $writer->namespaceMap = [ 'http://sabredav.org/ns' => null ]; $writer->openMemory(); $writer->startDocument('1.0'); $writer->setIndent(true); $writer->contextUri = 'http://example.org/'; $writer->write([ '{http://sabredav.org/ns}root' => [ '{http://sabredav.org/ns}uri' => new Uri('/foo/bar'), ] ]); $output = $writer->outputMemory(); $expected = << http://example.org/foo/bar XML; $this->assertEquals($expected, $output); } } sabre-xml-1.4.0/tests/Sabre/Xml/Element/XmlFragmentTest.php000066400000000000000000000111731266020770300235400ustar00rootroot00000000000000 $input BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}fragment' => 'Sabre\\Xml\\Element\\XmlFragment', ]; $reader->xml($input); $output = $reader->parse(); $this->assertEquals([ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}fragment', 'value' => new XmlFragment($expected), 'attributes' => [], ], ], 'attributes' => [], ], $output); } /** * Data provider for serialize and deserialize tests. * * Returns three items per test: * * 1. Input data for the reader. * 2. Expected output for XmlFragment deserializer * 3. Expected output after serializing that value again. * * If 3 is not set, use 1 for 3. * * @return void */ function xmlProvider() { return [ [ 'hello', 'hello', ], [ 'hello', 'hello' ], [ 'hello', 'hello' ], [ 'hello', 'hello' ], [ 'hello', 'hello', 'hello', ], [ 'hello', 'hello', 'hello', ], [ 'hello', 'hello', 'hello', ], [ 'hello', 'hello', 'hello', ], [ '', '', '', ], [ '', '', '', ], ]; } /** * @dataProvider xmlProvider */ function testSerialize($expectedFallback, $input, $expected = null) { if (is_null($expected)) { $expected = $expectedFallback; } $writer = new Writer(); $writer->namespaceMap = [ 'http://sabredav.org/ns' => null ]; $writer->openMemory(); $writer->startDocument('1.0'); //$writer->setIndent(true); $writer->write([ '{http://sabredav.org/ns}root' => [ '{http://sabredav.org/ns}fragment' => new XmlFragment($input), ], ]); $output = $writer->outputMemory(); $expected = << $expected XML; $this->assertEquals($expected, $output); } } sabre-xml-1.4.0/tests/Sabre/Xml/InfiteLoopTest.php000066400000000000000000000023731266020770300217750ustar00rootroot00000000000000 '; $reader = new Reader(); $reader->elementMap = [ '{DAV:}set' => 'Sabre\\Xml\\Element\\KeyValue', ]; $reader->xml($body); $output = $reader->parse(); $this->assertEquals([ 'name' => '{DAV:}propertyupdate', 'value' => [ [ 'name' => '{DAV:}set', 'value' => [ '{DAV:}prop' => null, ], 'attributes' => [], ], [ 'name' => '{DAV:}set', 'value' => [ '{DAV:}prop' => null, ], 'attributes' => [], ], ], 'attributes' => [], ], $output); } } sabre-xml-1.4.0/tests/Sabre/Xml/ReaderTest.php000066400000000000000000000302121266020770300211200ustar00rootroot00000000000000 BLA; $reader = new Reader(); $reader->xml($input); $reader->next(); $this->assertEquals('{http://sabredav.org/ns}root', $reader->getClark()); } function testGetClarkNoNS() { $input = << BLA; $reader = new Reader(); $reader->xml($input); $reader->next(); $this->assertEquals('{}root', $reader->getClark()); } function testGetClarkNotOnAnElement() { $input = << BLA; $reader = new Reader(); $reader->xml($input); $this->assertNull($reader->getClark()); } function testSimple() { $input = << Hi! BLA; $reader = new Reader(); $reader->xml($input); $output = $reader->parse(); $expected = [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => null, 'attributes' => [ 'attr' => 'val', ], ], [ 'name' => '{http://sabredav.org/ns}elem2', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem3', 'value' => 'Hi!', 'attributes' => [], ], ], 'attributes' => [], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $output); } function testCDATA() { $input = << BLA; $reader = new Reader(); $reader->xml($input); $output = $reader->parse(); $expected = [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}foo', 'value' => 'bar', 'attributes' => [], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $output); } function testSimpleNamespacedAttribute() { $input = << BLA; $reader = new Reader(); $reader->xml($input); $output = $reader->parse(); $expected = [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => null, 'attributes' => [ '{urn:foo}attr' => 'val', ], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $output); } function testMappedElement() { $input = << BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}elem1' => 'Sabre\\Xml\\Element\\Mock' ]; $reader->xml($input); $output = $reader->parse(); $expected = [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => 'foobar', 'attributes' => [], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $output); } /** * @expectedException \LogicException */ function testMappedElementBadClass() { $input = << BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}elem1' => new \StdClass() ]; $reader->xml($input); $reader->parse(); } /** * @depends testMappedElement */ function testMappedElementCallBack() { $input = << BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}elem1' => function(Reader $reader) { $reader->next(); return 'foobar'; } ]; $reader->xml($input); $output = $reader->parse(); $expected = [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => 'foobar', 'attributes' => [], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $output); } /** * @depends testMappedElementCallBack */ function testReadText() { $input = << hello world BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}elem1' => function(Reader $reader) { return $reader->readText(); } ]; $reader->xml($input); $output = $reader->parse(); $expected = [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => 'hello world', 'attributes' => [], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $output); } function testParseProblem() { $input = << BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}elem1' => 'Sabre\\Xml\\Element\\Mock' ]; $reader->xml($input); try { $output = $reader->parse(); $this->fail('We expected a ParseException to be thrown'); } catch (LibXMLException $e) { $this->assertInternalType('array', $e->getErrors()); } } /** * @expectedException \Sabre\Xml\ParseException */ function testBrokenParserClass() { $input = << BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}elem1' => 'Sabre\\Xml\\Element\\Eater' ]; $reader->xml($input); $reader->parse(); } /** * Test was added for Issue #10. * * @expectedException Sabre\Xml\LibXMLException */ function testBrokenXml() { $input = << BLA; $reader = new Reader(); $reader->xml($input); $reader->parse(); } /** * Test was added for Issue #45. * * @expectedException Sabre\Xml\LibXMLException */ function testBrokenXml2() { $input = << ""Administrative w"> xml($input); $reader->parse(); } /** * @depends testMappedElement */ function testParseInnerTree() { $input = << BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}elem1' => function(Reader $reader) { $innerTree = $reader->parseInnerTree(['{http://sabredav.org/ns}elem1' => function(Reader $reader) { $reader->next(); return "foobar"; }]); return $innerTree; } ]; $reader->xml($input); $output = $reader->parse(); $expected = [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => 'foobar', 'attributes' => [], ] ], 'attributes' => [], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $output); } /** * @depends testParseInnerTree */ function testParseGetElements() { $input = << BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}elem1' => function(Reader $reader) { $innerTree = $reader->parseGetElements(['{http://sabredav.org/ns}elem1' => function(Reader $reader) { $reader->next(); return "foobar"; }]); return $innerTree; } ]; $reader->xml($input); $output = $reader->parse(); $expected = [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => 'foobar', 'attributes' => [], ] ], 'attributes' => [], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $output); } /** * @depends testParseInnerTree */ function testParseGetElementsNoElements() { $input = << hi BLA; $reader = new Reader(); $reader->elementMap = [ '{http://sabredav.org/ns}elem1' => function(Reader $reader) { $innerTree = $reader->parseGetElements(['{http://sabredav.org/ns}elem1' => function(Reader $reader) { $reader->next(); return "foobar"; }]); return $innerTree; } ]; $reader->xml($input); $output = $reader->parse(); $expected = [ 'name' => '{http://sabredav.org/ns}root', 'value' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => [], 'attributes' => [], ], ], 'attributes' => [], ]; $this->assertEquals($expected, $output); } } sabre-xml-1.4.0/tests/Sabre/Xml/Serializer/000077500000000000000000000000001266020770300204605ustar00rootroot00000000000000sabre-xml-1.4.0/tests/Sabre/Xml/Serializer/EnumTest.php000066400000000000000000000011501266020770300227320ustar00rootroot00000000000000namespaceMap['urn:test'] = null; $xml = $service->write('{urn:test}root', function($writer) { enum($writer, [ '{urn:test}foo1', '{urn:test}foo2', ]); }); $expected = << XML; $this->assertXmlStringEqualsXmlString($expected, $xml); } } sabre-xml-1.4.0/tests/Sabre/Xml/Serializer/RepeatingElementsTest.php000066400000000000000000000012411266020770300254420ustar00rootroot00000000000000namespaceMap['urn:test'] = null; $xml = $service->write('{urn:test}collection', function($writer) { repeatingElements($writer, [ 'foo', 'bar', ], '{urn:test}item'); }); $expected = << foo bar XML; $this->assertXmlStringEqualsXmlString($expected, $xml); } } sabre-xml-1.4.0/tests/Sabre/Xml/ServiceTest.php000066400000000000000000000164271266020770300213320ustar00rootroot00000000000000 'Test!', ]; $util = new Service(); $util->elementMap = $elems; $reader = $util->getReader(); $this->assertInstanceOf('Sabre\\Xml\\Reader', $reader); $this->assertEquals($elems, $reader->elementMap); } function testGetWriter() { $ns = [ 'http://sabre.io/ns' => 's', ]; $util = new Service(); $util->namespaceMap = $ns; $writer = $util->getWriter(); $this->assertInstanceOf('Sabre\\Xml\\Writer', $writer); $this->assertEquals($ns, $writer->namespaceMap); } /** * @depends testGetReader */ function testParse() { $xml = << value XML; $util = new Service(); $result = $util->parse($xml, null, $rootElement); $this->assertEquals('{http://sabre.io/ns}root', $rootElement); $expected = [ [ 'name' => '{http://sabre.io/ns}child', 'value' => 'value', 'attributes' => [], ] ]; $this->assertEquals( $expected, $result ); } /** * @depends testGetReader */ function testParseStream() { $xml = << value XML; $stream = fopen('php://memory', 'r+'); fwrite($stream, $xml); rewind($stream); $util = new Service(); $result = $util->parse($stream, null, $rootElement); $this->assertEquals('{http://sabre.io/ns}root', $rootElement); $expected = [ [ 'name' => '{http://sabre.io/ns}child', 'value' => 'value', 'attributes' => [], ] ]; $this->assertEquals( $expected, $result ); } /** * @depends testGetReader */ function testExpect() { $xml = << value XML; $util = new Service(); $result = $util->expect('{http://sabre.io/ns}root', $xml); $expected = [ [ 'name' => '{http://sabre.io/ns}child', 'value' => 'value', 'attributes' => [], ] ]; $this->assertEquals( $expected, $result ); } /** * @depends testGetReader */ function testExpectStream() { $xml = << value XML; $stream = fopen('php://memory', 'r+'); fwrite($stream, $xml); rewind($stream); $util = new Service(); $result = $util->expect('{http://sabre.io/ns}root', $stream); $expected = [ [ 'name' => '{http://sabre.io/ns}child', 'value' => 'value', 'attributes' => [], ] ]; $this->assertEquals( $expected, $result ); } /** * @depends testGetReader * @expectedException \Sabre\Xml\ParseException */ function testExpectWrong() { $xml = << value XML; $util = new Service(); $util->expect('{http://sabre.io/ns}error', $xml); } /** * @depends testGetWriter */ function testWrite() { $util = new Service(); $util->namespaceMap = [ 'http://sabre.io/ns' => 's', ]; $result = $util->write('{http://sabre.io/ns}root', [ '{http://sabre.io/ns}child' => 'value', ]); $expected = << value XML; $this->assertEquals( $expected, $result ); } function testMapValueObject() { $input = << 1234 99.99 black friday deal 5 XML; $ns = 'http://sabredav.org/ns'; $orderService = new \Sabre\Xml\Service(); $orderService->mapValueObject('{' . $ns . '}order', 'Sabre\Xml\Order'); $orderService->mapValueObject('{' . $ns . '}status', 'Sabre\Xml\OrderStatus'); $orderService->namespaceMap[$ns] = null; $order = $orderService->parse($input); $expected = new Order(); $expected->id = 1234; $expected->amount = 99.99; $expected->description = 'black friday deal'; $expected->status = new OrderStatus(); $expected->status->id = 5; $expected->status->label = 'processed'; $this->assertEquals($expected, $order); $writtenXml = $orderService->writeValueObject($order); $this->assertEquals($input, $writtenXml); } function testMapValueObjectArrayProperty() { $input = << 1234 99.99 black friday deal 5 http://example.org/ http://example.com/ XML; $ns = 'http://sabredav.org/ns'; $orderService = new \Sabre\Xml\Service(); $orderService->mapValueObject('{' . $ns . '}order', 'Sabre\Xml\Order'); $orderService->mapValueObject('{' . $ns . '}status', 'Sabre\Xml\OrderStatus'); $orderService->namespaceMap[$ns] = null; $order = $orderService->parse($input); $expected = new Order(); $expected->id = 1234; $expected->amount = 99.99; $expected->description = 'black friday deal'; $expected->status = new OrderStatus(); $expected->status->id = 5; $expected->status->label = 'processed'; $expected->link = ['http://example.org/', 'http://example.com/']; $this->assertEquals($expected, $order); $writtenXml = $orderService->writeValueObject($order); $this->assertEquals($input, $writtenXml); } /** * @expectedException \InvalidArgumentException */ function testWriteVoNotFound() { $service = new Service(); $service->writeValueObject(new \StdClass()); } function testParseClarkNotation() { $this->assertEquals([ 'http://sabredav.org/ns', 'elem', ], Service::parseClarkNotation('{http://sabredav.org/ns}elem')); } /** * @expectedException \InvalidArgumentException */ function testParseClarkNotationFail() { Service::parseClarkNotation('http://sabredav.org/ns}elem'); } } /** * asset for testMapValueObject() * @internal */ class Order { public $id; public $amount; public $description; public $status; public $empty; public $link = []; } /** * asset for testMapValueObject() * @internal */ class OrderStatus { public $id; public $label; } sabre-xml-1.4.0/tests/Sabre/Xml/WriterTest.php000066400000000000000000000227421266020770300212030ustar00rootroot00000000000000writer = new Writer(); $this->writer->namespaceMap = [ 'http://sabredav.org/ns' => 's', ]; $this->writer->openMemory(); $this->writer->setIndent(true); $this->writer->startDocument(); } function compare($input, $output) { $this->writer->write($input); $this->assertEquals($output, $this->writer->outputMemory()); } function testSimple() { $this->compare([ '{http://sabredav.org/ns}root' => 'text', ], << text HI ); } /** * @depends testSimple */ function testSimpleQuotes() { $this->compare([ '{http://sabredav.org/ns}root' => '"text"', ], << "text" HI ); } function testSimpleAttributes() { $this->compare([ '{http://sabredav.org/ns}root' => [ 'value' => 'text', 'attributes' => [ 'attr1' => 'attribute value', ], ], ], << text HI ); } function testMixedSyntax() { $this->compare([ '{http://sabredav.org/ns}root' => [ '{http://sabredav.org/ns}single' => 'value', '{http://sabredav.org/ns}multiple' => [ [ 'name' => '{http://sabredav.org/ns}foo', 'value' => 'bar', ], [ 'name' => '{http://sabredav.org/ns}foo', 'value' => 'foobar', ], ], [ 'name' => '{http://sabredav.org/ns}attributes', 'value' => null, 'attributes' => [ 'foo' => 'bar', ], ], [ 'name' => '{http://sabredav.org/ns}verbose', 'value' => 'syntax', 'attributes' => [ 'foo' => 'bar', ], ], ], ], << value bar foobar syntax HI ); } function testNull() { $this->compare([ '{http://sabredav.org/ns}root' => null, ], << HI ); } function testArrayFormat2() { $this->compare([ '{http://sabredav.org/ns}root' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => 'text', 'attributes' => [ 'attr1' => 'attribute value', ], ], ], ], << text HI ); } function testArrayOfValues() { $this->compare([ '{http://sabredav.org/ns}root' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => [ 'foo', 'bar', 'baz', ], ], ], ], << foobarbaz HI ); } /** * @depends testArrayFormat2 */ function testArrayFormat2NoValue() { $this->compare([ '{http://sabredav.org/ns}root' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'attributes' => [ 'attr1' => 'attribute value', ], ], ], ], << HI ); } function testCustomNamespace() { $this->compare([ '{http://sabredav.org/ns}root' => [ '{urn:foo}elem1' => 'bar', ], ], << bar HI ); } function testEmptyNamespace() { // Empty namespaces are allowed, so we should support this. $this->compare([ '{http://sabredav.org/ns}root' => [ '{}elem1' => 'bar', ], ], << bar HI ); } function testAttributes() { $this->compare([ '{http://sabredav.org/ns}root' => [ [ 'name' => '{http://sabredav.org/ns}elem1', 'value' => 'text', 'attributes' => [ 'attr1' => 'val1', '{http://sabredav.org/ns}attr2' => 'val2', '{urn:foo}attr3' => 'val3', ], ], ], ], << text HI ); } function testBaseElement() { $this->compare([ '{http://sabredav.org/ns}root' => new Element\Base('hello') ], << hello HI ); } function testElementObj() { $this->compare([ '{http://sabredav.org/ns}root' => new Element\Mock() ], << hiiii! HI ); } function testEmptyNamespacePrefix() { $this->writer->namespaceMap['http://sabredav.org/ns'] = null; $this->compare([ '{http://sabredav.org/ns}root' => new Element\Mock() ], << hiiii! HI ); } function testEmptyNamespacePrefixEmptyString() { $this->writer->namespaceMap['http://sabredav.org/ns'] = ''; $this->compare([ '{http://sabredav.org/ns}root' => new Element\Mock() ], << hiiii! HI ); } function testWriteElement() { $this->writer->writeElement("{http://sabredav.org/ns}foo", 'content'); $output = << content HI; $this->assertEquals($output, $this->writer->outputMemory()); } function testWriteElementComplex() { $this->writer->writeElement("{http://sabredav.org/ns}foo", new Element\KeyValue(['{http://sabredav.org/ns}bar' => 'test'])); $output = << test HI; $this->assertEquals($output, $this->writer->outputMemory()); } /** * @expectedException \InvalidArgumentException */ function testWriteBadObject() { $this->writer->write(new \StdClass()); } function testStartElementSimple() { $this->writer->startElement("foo"); $this->writer->endElement(); $output = << HI; $this->assertEquals($output, $this->writer->outputMemory()); } function testCallback() { $this->compare([ '{http://sabredav.org/ns}root' => function(Writer $writer) { $writer->text('deferred writer'); }, ], << deferred writer HI ); } /** * @expectedException \InvalidArgumentException */ function testResource() { $this->compare([ '{http://sabredav.org/ns}root' => fopen('php://memory', 'r'), ], << deferred writer HI ); } function testClassMap() { $obj = (object)[ 'key1' => 'value1', 'key2' => 'value2', ]; $this->writer->classMap['stdClass'] = function(Writer $writer, $value) { foreach (get_object_vars($value) as $key => $val) { $writer->writeElement('{http://sabredav.org/ns}' . $key, $val); } }; $this->compare([ '{http://sabredav.org/ns}root' => $obj ], << value1 value2 HI ); } } sabre-xml-1.4.0/tests/bootstrap.php000066400000000000000000000005051266020770300173010ustar00rootroot00000000000000 sabre.io codesniffer ruleset sabre-xml-1.4.0/tests/phpunit.xml.dist000066400000000000000000000006231266020770300177270ustar00rootroot00000000000000 Sabre/ ../lib/