commit vendor

This commit is contained in:
2025-11-11 14:49:30 +01:00
parent f33121a308
commit 6d03080c00
2436 changed files with 483781 additions and 0 deletions

View File

@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
/**
* Context Stack.
*
* The Context maintains information about a document during either reading or
* writing.
*
* During this process, it may be neccesary to override this context
* information.
*
* This trait allows easy access to the context, and allows the end-user to
* override its settings for document fragments, and easily restore it again
* later.
*
* @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
trait ContextStackTrait
{
/**
* This is the element map. It contains a list of XML elements (in clark
* notation) as keys and PHP class names as values.
*
* The PHP class names must implement Sabre\Xml\Element.
*
* Values may also be a callable. In that case the function will be called
* directly.
*
* @var array
*/
public $elementMap = [];
/**
* A contextUri pointing to the document being parsed / written.
* This uri may be used to resolve relative urls that may appear in the
* document.
*
* The reader and writer don't use this property, but as it's an extremely
* common use-case for parsing XML documents, it's added here as a
* convenience.
*
* @var string|null
*/
public $contextUri;
/**
* This is a list of namespaces that you want to give default prefixes.
*
* You must make sure you create this entire list before starting to write.
* They should be registered on the root element.
*
* @var array
*/
public $namespaceMap = [];
/**
* This is a list of custom serializers for specific classes.
*
* The writer may use this if you attempt to serialize an object with a
* class that does not implement XmlSerializable.
*
* Instead it will look at this classmap to see if there is a custom
* serializer here. This is useful if you don't want your value objects
* to be responsible for serializing themselves.
*
* The keys in this classmap need to be fully qualified PHP class names,
* the values must be callbacks. The callbacks take two arguments. The
* writer class, and the value that must be written.
*
* function (Writer $writer, object $value)
*
* @var array
*/
public $classMap = [];
/**
* Backups of previous contexts.
*
* @var array
*/
protected $contextStack = [];
/**
* Create a new "context".
*
* This allows you to safely modify the elementMap, contextUri or
* namespaceMap. After you're done, you can restore the old data again
* with popContext.
*/
public function pushContext()
{
$this->contextStack[] = [
$this->elementMap,
$this->contextUri,
$this->namespaceMap,
$this->classMap,
];
}
/**
* Restore the previous "context".
*/
public function popContext()
{
list(
$this->elementMap,
$this->contextUri,
$this->namespaceMap,
$this->classMap
) = array_pop($this->contextStack);
}
}

View File

@ -0,0 +1,359 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml\Deserializer;
use Sabre\Xml\Reader;
/**
* This class provides a number of 'deserializer' helper functions.
* These can be used to easily specify custom deserializers for specific
* XML elements.
*
* You can either use these functions from within the $elementMap in the
* Service or Reader class, or you can call them from within your own
* deserializer functions.
*/
/**
* The 'keyValue' deserializer parses all child elements, and outputs them as
* a "key=>value" array.
*
* For example, keyvalue will parse:
*
* <?xml version="1.0"?>
* <s:root xmlns:s="http://sabredav.org/ns">
* <s:elem1>value1</s:elem1>
* <s:elem2>value2</s:elem2>
* <s:elem3 />
* </s:root>
*
* 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.
*/
function keyValue(Reader $reader, string $namespace = null): array
{
// If there's no children, we don't do anything.
if ($reader->isEmptyElement) {
$reader->next();
return [];
}
if (!$reader->read()) {
$reader->next();
return [];
}
if (Reader::END_ELEMENT === $reader->nodeType) {
$reader->next();
return [];
}
$values = [];
do {
if (Reader::ELEMENT === $reader->nodeType) {
if (null !== $namespace && $reader->namespaceURI === $namespace) {
$values[$reader->localName] = $reader->parseCurrentElement()['value'];
} else {
$clark = $reader->getClark();
$values[$clark] = $reader->parseCurrentElement()['value'];
}
} else {
if (!$reader->read()) {
break;
}
}
} while (Reader::END_ELEMENT !== $reader->nodeType);
$reader->read();
return $values;
}
/**
* The 'enum' deserializer parses elements into a simple list
* without values or attributes.
*
* For example, Elements will parse:
*
* <?xml version="1.0"? >
* <s:root xmlns:s="http://sabredav.org/ns">
* <s:elem1 />
* <s:elem2 />
* <s:elem3 />
* <s:elem4>content</s:elem4>
* <s:elem5 attr="val" />
* </s:root>
*
* 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",
* ];
*
* @return string[]
*/
function enum(Reader $reader, string $namespace = null): array
{
// If there's no children, we don't do anything.
if ($reader->isEmptyElement) {
$reader->next();
return [];
}
if (!$reader->read()) {
$reader->next();
return [];
}
if (Reader::END_ELEMENT === $reader->nodeType) {
$reader->next();
return [];
}
$currentDepth = $reader->depth;
$values = [];
do {
if (Reader::ELEMENT !== $reader->nodeType) {
continue;
}
if (!is_null($namespace) && $namespace === $reader->namespaceURI) {
$values[] = $reader->localName;
} else {
$values[] = (string) $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.
*
* @return object
*/
function valueObject(Reader $reader, string $className, string $namespace)
{
$valueObject = new $className();
if ($reader->isEmptyElement) {
$reader->next();
return $valueObject;
}
$defaultProperties = get_class_vars($className);
$reader->read();
do {
if (Reader::ELEMENT === $reader->nodeType && $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 {
if (!$reader->read()) {
break;
}
}
} while (Reader::END_ELEMENT !== $reader->nodeType);
$reader->read();
return $valueObject;
}
/**
* This deserializer helps you deserialize xml structures that look like
* this:.
*
* <collection>
* <item>...</item>
* <item>...</item>
* <item>...</item>
* </collection>
*
* 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.
*
* $childElementName must either be a a clark-notation element name, or if no
* namespace is used, the bare element name.
*/
function repeatingElements(Reader $reader, string $childElementName): array
{
if ('{' !== $childElementName[0]) {
$childElementName = '{}'.$childElementName;
}
$result = [];
foreach ($reader->parseGetElements() as $element) {
if ($element['name'] === $childElementName) {
$result[] = $element['value'];
}
}
return $result;
}
/**
* This deserializer helps you to deserialize structures which contain mixed content like this:.
*
* <p>some text <extref>and a inline tag</extref>and even more text</p>
*
* The above example will return
*
* [
* 'some text',
* [
* 'name' => '{}extref',
* 'value' => 'and a inline tag',
* 'attributes' => []
* ],
* 'and even more text'
* ]
*
* In strict XML documents you wont find this kind of markup but in html this is a quite common pattern.
*/
function mixedContent(Reader $reader): array
{
// If there's no children, we don't do anything.
if ($reader->isEmptyElement) {
$reader->next();
return [];
}
$previousDepth = $reader->depth;
$content = [];
$reader->read();
while (true) {
if (Reader::ELEMENT == $reader->nodeType) {
$content[] = $reader->parseCurrentElement();
} elseif ($reader->depth >= $previousDepth && in_array($reader->nodeType, [Reader::TEXT, Reader::CDATA, Reader::WHITESPACE])) {
$content[] = $reader->value;
$reader->read();
} elseif (Reader::END_ELEMENT == $reader->nodeType) {
// Ensuring we are moving the cursor after the end element.
$reader->read();
break;
} else {
$reader->read();
}
}
return $content;
}
/**
* The functionCaller deserializer turns an xml element into whatever your callable return.
*
* You can use, e.g., a named constructor (factory method) to create an object using
* this function.
*
* @return mixed
*/
function functionCaller(Reader $reader, callable $func, string $namespace)
{
if ($reader->isEmptyElement) {
$reader->next();
return null;
}
$funcArgs = [];
$func = is_string($func) && false !== strpos($func, '::') ? explode('::', $func) : $func;
$ref = is_array($func) ? new \ReflectionMethod($func[0], $func[1]) : new \ReflectionFunction($func);
foreach ($ref->getParameters() as $parameter) {
$funcArgs[$parameter->getName()] = null;
}
$reader->read();
do {
if (Reader::ELEMENT === $reader->nodeType && $reader->namespaceURI == $namespace) {
if (array_key_exists($reader->localName, $funcArgs)) {
$funcArgs[$reader->localName] = $reader->parseCurrentElement()['value'];
} else {
// Ignore property
$reader->next();
}
} else {
$reader->read();
}
} while (Reader::END_ELEMENT !== $reader->nodeType);
$reader->read();
return $func(...array_values($funcArgs));
}

22
vendor/sabre/xml/lib/Element.php vendored Normal file
View File

@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
/**
* This is the XML element interface.
*
* Elements are responsible for serializing and deserializing part of an XML
* document into PHP values.
*
* It combines XmlSerializable and XmlDeserializable into one logical class
* that does both.
*
* @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface Element extends XmlSerializable, XmlDeserializable
{
}

84
vendor/sabre/xml/lib/Element/Base.php vendored Normal file
View File

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml\Element;
use Sabre\Xml;
/**
* The Base XML element is the standard parser & generator that's used by the
* XML reader and writer.
*
* It spits out a simple PHP array structure during deserialization, that can
* also be directly injected back into Writer::write.
*
* @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 Base implements Xml\Element
{
/**
* PHP value to serialize.
*
* @var mixed
*/
protected $value;
/**
* Constructor.
*/
public function __construct($value = null)
{
$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 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.
*/
public 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.
*
* @return mixed
*/
public static function xmlDeserialize(Xml\Reader $reader)
{
$subTree = $reader->parseInnerTree();
return $subTree;
}
}

59
vendor/sabre/xml/lib/Element/Cdata.php vendored Normal file
View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml\Element;
use Sabre\Xml;
/**
* CDATA element.
*
* This element allows you to easily inject CDATA.
*
* Note that we strongly recommend avoiding CDATA nodes, unless you definitely
* know what you're doing, or you're working with unchangable systems that
* require CDATA.
*
* @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 Cdata implements Xml\XmlSerializable
{
/**
* CDATA element value.
*
* @var string
*/
protected $value;
/**
* Constructor.
*/
public function __construct(string $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.
*/
public function xmlSerialize(Xml\Writer $writer)
{
$writer->writeCData($this->value);
}
}

View File

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml\Element;
use Sabre\Xml;
use Sabre\Xml\Deserializer;
use Sabre\Xml\Serializer;
/**
* 'Elements' is a simple list of elements, without values or attributes.
* For example, Elements will parse:.
*
* <?xml version="1.0"?>
* <s:root xmlns:s="http://sabredav.org/ns">
* <s:elem1 />
* <s:elem2 />
* <s:elem3 />
* <s:elem4>content</s:elem4>
* <s:elem5 attr="val" />
* </s:root>
*
* 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.
*/
public 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.
*/
public 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.
*
* @return mixed
*/
public static function xmlDeserialize(Xml\Reader $reader)
{
return Deserializer\enum($reader);
}
}

View File

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml\Element;
use Sabre\Xml;
use Sabre\Xml\Deserializer;
/**
* 'KeyValue' parses out all child elements from a single node, and outputs a
* key=>value 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:
*
* <?xml version="1.0"?>
* <s:root xmlns:s="http://sabredav.org/ns">
* <s:elem1>value1</s:elem1>
* <s:elem2>value2</s:elem2>
* <s:elem3 />
* </s:root>
*
* 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.
*/
public 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.
*/
public 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.
*
* @return mixed
*/
public static function xmlDeserialize(Xml\Reader $reader)
{
return Deserializer\keyValue($reader);
}
}

99
vendor/sabre/xml/lib/Element/Uri.php vendored Normal file
View File

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml\Element;
use Sabre\Xml;
/**
* Uri element.
*
* This represents a single uri. An example of how this may be encoded:
*
* <link>/foo/bar</link>
* <d:href xmlns:d="DAV:">http://example.org/hi</d:href>
*
* 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
*/
public 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.
*/
public 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.
*
* @return mixed
*/
public static function xmlDeserialize(Xml\Reader $reader)
{
return new self(
\Sabre\Uri\resolve(
(string) $reader->contextUri,
$reader->readText()
)
);
}
}

View File

@ -0,0 +1,148 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml\Element;
use Sabre\Xml\Element;
use Sabre\Xml\Reader;
use Sabre\Xml\Writer;
/**
* The XmlFragment element allows you to extract a portion of your xml tree,
* and get a well-formed xml string.
*
* This goes a bit beyond `innerXml` and friends, as we'll also match all the
* correct namespaces.
*
* Please note that the XML fragment:
*
* 1. Will not have an <?xml declaration.
* 2. Or a DTD
* 3. It will have all the relevant xmlns attributes.
* 4. It may not have a root element.
*/
class XmlFragment implements Element
{
/**
* The inner XML value.
*
* @var string
*/
protected $xml;
/**
* Constructor.
*/
public function __construct(string $xml)
{
$this->xml = $xml;
}
/**
* Returns the inner XML document.
*/
public function getXml(): string
{
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.
*/
public function xmlSerialize(Writer $writer)
{
$reader = new Reader();
// Wrapping the xml in a container, so root-less values can still be
// parsed.
$xml = <<<XML
<?xml version="1.0"?>
<xml-fragment xmlns="http://sabre.io/ns">{$this->getXml()}</xml-fragment>
XML;
$reader->xml($xml);
while ($reader->read()) {
if ($reader->depth < 1) {
// Skipping the root node.
continue;
}
switch ($reader->nodeType) {
case Reader::ELEMENT:
$writer->startElement(
(string) $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((string) $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.
*
* @return mixed
*/
public static function xmlDeserialize(Reader $reader)
{
$result = new self($reader->readInnerXml());
$reader->next();
return $result;
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
use LibXMLError;
use Throwable;
/**
* This exception is thrown when the Readers runs into a parsing error.
*
* This exception effectively wraps 1 or more LibXMLError objects.
*
* @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 LibXMLException extends ParseException
{
/**
* The error list.
*
* @var \LibXMLError[]
*/
protected $errors;
/**
* Creates the exception.
*
* You should pass a list of LibXMLError objects in its constructor.
*
* @param LibXMLError[] $errors
* @param Throwable $previousException
*/
public function __construct(array $errors, int $code = 0, Throwable $previousException = null)
{
$this->errors = $errors;
parent::__construct($errors[0]->message.' on line '.$errors[0]->line.', column '.$errors[0]->column, $code, $previousException);
}
/**
* Returns the LibXML errors.
*/
public function getErrors(): array
{
return $this->errors;
}
}

19
vendor/sabre/xml/lib/ParseException.php vendored Normal file
View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
use
Exception;
/**
* This is a base exception for any exception related to parsing xml files.
*
* @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 ParseException extends Exception
{
}

301
vendor/sabre/xml/lib/Reader.php vendored Normal file
View File

@ -0,0 +1,301 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
use XMLReader;
/**
* The Reader class expands upon PHP's built-in XMLReader.
*
* The intended usage, is to assign certain XML elements to PHP classes. These
* need to be registered using the $elementMap public property.
*
* After this is done, a single call to parse() will parse the entire document,
* and delegate sub-sections of the document to element classes.
*
* @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 Reader extends XMLReader
{
use ContextStackTrait;
/**
* Returns the current nodename in clark-notation.
*
* For example: "{http://www.w3.org/2005/Atom}feed".
* Or if no namespace is defined: "{}feed".
*
* This method returns null if we're not currently on an element.
*
* @return string|null
*/
public function getClark()
{
if (!$this->localName) {
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.
*/
public function parse(): array
{
$previousEntityState = libxml_disable_entity_loader(true);
$previousSetting = libxml_use_internal_errors(true);
try {
while (self::ELEMENT !== $this->nodeType) {
if (!$this->read()) {
$errors = libxml_get_errors();
libxml_clear_errors();
if ($errors) {
throw new LibXMLException($errors);
}
}
}
$result = $this->parseCurrentElement();
// last line of defense in case errors did occur above
$errors = libxml_get_errors();
libxml_clear_errors();
if ($errors) {
throw new LibXMLException($errors);
}
} finally {
libxml_use_internal_errors($previousSetting);
libxml_disable_entity_loader($previousEntityState);
}
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.
*/
public function parseGetElements(array $elementMap = null): array
{
$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.
*
* @return array|string|null
*/
public function parseInnerTree(array $elementMap = null)
{
$text = null;
$elements = [];
if (self::ELEMENT === $this->nodeType && $this->isEmptyElement) {
// Easy!
$this->next();
return null;
}
if (!is_null($elementMap)) {
$this->pushContext();
$this->elementMap = $elementMap;
}
try {
if (!$this->read()) {
$errors = libxml_get_errors();
libxml_clear_errors();
if ($errors) {
throw new LibXMLException($errors);
}
throw new ParseException('This should never happen (famous last words)');
}
$keepOnParsing = true;
while ($keepOnParsing) {
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();
$keepOnParsing = false;
break;
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;
}
}
} finally {
if (!is_null($elementMap)) {
$this->popContext();
}
}
return $elements ? $elements : $text;
}
/**
* Reads all text below the current element, and returns this as a string.
*/
public function readText(): string
{
$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.
*/
public function parseCurrentElement(): array
{
$name = $this->getClark();
$attributes = [];
if ($this->hasAttributes) {
$attributes = $this->parseAttributes();
}
$value = call_user_func(
$this->getDeserializerForElementName((string) $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.
*/
public function parseAttributes(): array
{
$attributes = [];
while ($this->moveToNextAttribute()) {
if ($this->namespaceURI) {
// Ignoring 'xmlns', it doesn't make any sense.
if ('http://www.w3.org/2000/xmlns/' === $this->namespaceURI) {
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.
*/
public function getDeserializerForElementName(string $name): callable
{
if (!array_key_exists($name, $this->elementMap)) {
if ('{}' == substr($name, 0, 2) && array_key_exists(substr($name, 2), $this->elementMap)) {
$name = substr($name, 2);
} else {
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 ('string' === $type) {
$type .= ' ('.$deserializer.')';
} elseif ('object' === $type) {
$type .= ' ('.get_class($deserializer).')';
}
throw new \LogicException('Could not use this type as a deserializer: '.$type.' for element: '.$name);
}
}

View File

@ -0,0 +1,208 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml\Serializer;
use InvalidArgumentException;
use Sabre\Xml\Writer;
use Sabre\Xml\XmlSerializable;
/**
* This file provides a number of 'serializer' helper functions.
*
* These helper functions can be used to easily xml-encode common PHP
* data structures, or can be placed in the $classMap.
*/
/**
* The 'enum' serializer writes simple list of elements.
*
* For example, calling:
*
* enum($writer, [
* "{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",
* ]);
*
* Will generate something like this (if the correct namespace is declared):
*
* <s:elem1 />
* <s:elem2 />
* <s:elem3 />
* <s:elem4>content</s:elem4>
* <s:elem5 attr="val" />
*
* @param string[] $values
*/
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 object $valueObject
*/
function valueObject(Writer $writer, $valueObject, string $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 (null !== $val) {
$writer->writeElement('{'.$namespace.'}'.$key, $val);
}
}
}
/**
* This serializer helps you serialize xml structures that look like
* this:.
*
* <collection>
* <item>...</item>
* <item>...</item>
* <item>...</item>
* </collection>
*
* In that previous example, this serializer just serializes the item element,
* and this could be called like this:
*
* repeatingElements($writer, $items, '{}item');
*/
function repeatingElements(Writer $writer, array $items, string $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:
*
* <element-name xmlns="namespaceUri" attName="attValue">
* ...
* </element-name>
* <element-name2>
* ...
* </element-name2>
*
* 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 string|int|float|bool|array|object $value
*/
function standardSerializer(Writer $writer, $value)
{
if (is_scalar($value)) {
// String, integer, float, boolean
$writer->text((string) $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_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));
} elseif (!is_null($value)) {
throw new InvalidArgumentException('The writer cannot serialize values of type: '.gettype($value));
}
}

310
vendor/sabre/xml/lib/Service.php vendored Normal file
View File

@ -0,0 +1,310 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
/**
* XML parsing and writing service.
*
* You are encouraged to make a instance of this for your application and
* potentially extend it, as a central API point for dealing with xml and
* configuring the reader and 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 Service
{
/**
* This is the element map. It contains a list of XML elements (in clark
* notation) as keys and PHP class names as values.
*
* The PHP class names must implement Sabre\Xml\Element.
*
* Values may also be a callable. In that case the function will be called
* directly.
*
* @var array
*/
public $elementMap = [];
/**
* This is a list of namespaces that you want to give default prefixes.
*
* You must make sure you create this entire list before starting to write.
* They should be registered on the root element.
*
* @var array
*/
public $namespaceMap = [];
/**
* This is a list of custom serializers for specific classes.
*
* The writer may use this if you attempt to serialize an object with a
* class that does not implement XmlSerializable.
*
* Instead it will look at this classmap to see if there is a custom
* serializer here. This is useful if you don't want your value objects
* to be responsible for serializing themselves.
*
* The keys in this classmap need to be fully qualified PHP class names,
* the values must be callbacks. The callbacks take two arguments. The
* writer class, and the value that must be written.
*
* function (Writer $writer, object $value)
*
* @var array
*/
public $classMap = [];
/**
* A bitmask of the LIBXML_* constants.
*
* @var int
*/
public $options = 0;
/**
* Returns a fresh XML Reader.
*/
public function getReader(): Reader
{
$r = new Reader();
$r->elementMap = $this->elementMap;
return $r;
}
/**
* Returns a fresh xml writer.
*/
public function getWriter(): Writer
{
$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
*
* @throws ParseException
*
* @return array|object|string
*/
public function parse($input, string $contextUri = null, string &$rootElementName = null)
{
if (is_resource($input)) {
// Unfortunately the XMLReader doesn't support streams. When it
// does, we can optimize this.
$input = (string) stream_get_contents($input);
// If input is an empty string, then its safe to throw exception
if ('' === $input) {
throw new ParseException('The input element to parse is empty. Do not attempt to parse');
}
}
$r = $this->getReader();
$r->contextUri = $contextUri;
$r->XML($input, null, $this->options);
$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
*
* @throws ParseException
*
* @return array|object|string
*/
public function expect($rootElementName, $input, string $contextUri = null)
{
if (is_resource($input)) {
// Unfortunately the XMLReader doesn't support streams. When it
// does, we can optimize this.
$input = (string) stream_get_contents($input);
// If input is empty string, then its safe to throw exception
if ('' === $input) {
throw new ParseException('The input element to parse is empty. Do not attempt to parse');
}
}
$r = $this->getReader();
$r->contextUri = $contextUri;
$r->XML($input, null, $this->options);
$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 ', $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|array|object|XmlSerializable $value
*
* @return string
*/
public function write(string $rootElementName, $value, string $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:
*
* <author xmlns="http://example.org/ns">
* <firstName>...</firstName>
* <lastName>...</lastName>
* </author>
*
* These can easily be mapped by calling:
*
* $service->mapValueObject('{http://example.org}author', 'Author');
*/
public function mapValueObject(string $elementName, string $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
*
* @throws \InvalidArgumentException
*/
public function writeValueObject($object, string $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.
*
* @throws \InvalidArgumentException
*/
public static function parseClarkNotation(string $str): array
{
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 = [];
}

20
vendor/sabre/xml/lib/Version.php vendored Normal file
View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
/**
* This class contains the version number for this package.
*
* @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/
*/
class Version
{
/**
* Full version number.
*/
const VERSION = '2.2.0';
}

257
vendor/sabre/xml/lib/Writer.php vendored Normal file
View File

@ -0,0 +1,257 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
use XMLWriter;
/**
* The XML Writer class.
*
* This class works exactly as PHP's built-in XMLWriter, with a few additions.
*
* Namespaces can be registered beforehand, globally. When the first element is
* written, namespaces will automatically be declared.
*
* The writeAttribute, startElement and writeElement can now take a
* clark-notation element name (example: {http://www.w3.org/2005/Atom}link).
*
* If, when writing the namespace is a known one a prefix will automatically be
* selected, otherwise a random prefix will be generated.
*
* Instead of standard string values, the writer can take Element classes (as
* defined by this library) to delegate the serialization.
*
* The write() method can take array structures to quickly write out simple xml
* trees.
*
* @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 Writer extends XMLWriter
{
use ContextStackTrait;
/**
* Any namespace that the writer is asked to write, will be added here.
*
* Any of these elements will get a new namespace definition *every single
* time* they are used, but this array allows the writer to make sure that
* the prefixes are consistent anyway.
*
* @var array
*/
protected $adhocNamespaces = [];
/**
* When the first element is written, this flag is set to true.
*
* This ensures that the namespaces in the namespaces map are only written
* once.
*
* @var bool
*/
protected $namespacesWritten = false;
/**
* Writes a value to the output stream.
*
* The following values are supported:
* 1. Scalar values will be written as-is, as text.
* 2. Null values will be skipped (resulting in a short xml tag).
* 3. If a value is an instance of an Element class, writing will be
* delegated to the object.
* 4. If a value is an array, two formats are supported.
*
* Array format 1:
* [
* "{namespace}name1" => "..",
* "{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
*/
public 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:
*
* <entry xmlns="http://w3.org/2005/Atom">
*
* Note: this function doesn't have the string typehint, because PHP's
* XMLWriter::startElement doesn't either.
*
* @param string $name
*/
public function startElement($name): bool
{
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 || null === $namespace) {
$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:
* <author xmlns="http://www.w3.org/2005" />
*
* $writer->writeElement('{http://www.w3.org/2005/Atom}author', [
* '{http://www.w3.org/2005/Atom}name' => 'Evert Pot',
* ]);
* becomes:
* <author xmlns="http://www.w3.org/2005" /><name>Evert Pot</name></author>
*
* Note: this function doesn't have the string typehint, because PHP's
* XMLWriter::startElement doesn't either.
*
* @param array|string|object|null $content
*/
public function writeElement($name, $content = null): bool
{
$this->startElement($name);
if (!is_null($content)) {
$this->write($content);
}
$this->endElement();
return true;
}
/**
* 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.
*/
public 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.
*
* Note: this function doesn't have typehints, because for some reason
* PHP's XMLWriter::writeAttribute doesn't either.
*
* @param string $name
* @param string $value
*/
public function writeAttribute($name, $value): bool
{
if ('{' !== $name[0]) {
return parent::writeAttribute($name, $value);
}
list(
$namespace,
$localName
) = Service::parseClarkNotation($name);
if (array_key_exists($namespace, $this->namespaceMap)) {
// It's an attribute with a namespace we know
return $this->writeAttribute(
$this->namespaceMap[$namespace].':'.$localName,
$value
);
}
// 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);
}
return $this->writeAttributeNS(
$this->adhocNamespaces[$namespace],
$localName,
$namespace,
$value
);
}
}

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
/**
* Implementing the XmlDeserializable interface allows you to use a class as a
* deserializer for a specific element.
*
* @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface XmlDeserializable
{
/**
* The deserialize 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.
*
* 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.
*
* @return mixed
*/
public static function xmlDeserialize(Reader $reader);
}

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Sabre\Xml;
/**
* Objects implementing XmlSerializable can control how they are represented in
* Xml.
*
* @copyright Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/).
* @author Evert Pot (http://evertpot.com/)
* @license http://sabre.io/license/ Modified BSD License
*/
interface XmlSerializable
{
/**
* The xmlSerialize method 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.
*/
public function xmlSerialize(Writer $writer);
}