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