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

27
vendor/sabre/xml/LICENSE vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (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.

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);
}

6
vendor/sabre/xml/phpstan.neon vendored Normal file
View File

@ -0,0 +1,6 @@
parameters:
level: 5
ignoreErrors:
-
message: '!Parameter #3 \$uri of method XMLWriter::startElementNs\(\) expects string, null given.!'
path: lib/Writer.php