1<?php declare(strict_types=1);
2
3/**
4* @package   s9e\SweetDOM
5* @copyright Copyright (c) 2019-2020 The s9e authors
6* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7*/
8namespace s9e\SweetDOM;
9
10use DOMDocument;
11use DOMNode;
12use DOMNodeList;
13use DOMXPath;
14
15class Document extends DOMDocument
16{
17	/**
18	* @link https://www.php.net/manual/domdocument.construct.php
19	*
20	* @param string $version  Version number of the document
21	* @param string $encoding Encoding of the document
22	*/
23	public function __construct(string $version = '1.0', string $encoding = 'utf-8')
24	{
25		parent::__construct($version, $encoding);
26
27		$this->registerNodeClass('DOMElement', Element::class);
28	}
29
30	/**
31	* Create and return an xsl:apply-templates element
32	*
33	* @param  string  $select XPath expression for the "select" attribute
34	* @return Element
35	*/
36	public function createXslApplyTemplates(string $select = null): Element
37	{
38		$element = $this->createElementXSL('apply-templates');
39		if (isset($select))
40		{
41			$element->setAttribute('select', $select);
42		}
43
44		return $element;
45	}
46
47	/**
48	* Create and return an xsl:attribute element
49	*
50	* @param  string  $name Attribute's name
51	* @param  string  $text Text content for the element
52	* @return Element
53	*/
54	public function createXslAttribute(string $name, string $text = ''): Element
55	{
56		$element = $this->createElementXSL('attribute', $text);
57		$element->setAttribute('name', $name);
58
59		return $element;
60	}
61
62	/**
63	* Create and return an xsl:choose element
64	*
65	* @return Element
66	*/
67	public function createXslChoose(): Element
68	{
69		return $this->createElementXSL('choose');
70	}
71
72	/**
73	* Create and return an xsl:comment element
74	*
75	* @param  string  $text Text content for the comment
76	* @return Element
77	*/
78	public function createXslComment(string $text = ''): Element
79	{
80		return $this->createElementXSL('comment', $text);
81	}
82
83	/**
84	* Create and return an xsl:copy-of element
85	*
86	* @param  string  $select XPath expression for the "select" attribute
87	* @return Element
88	*/
89	public function createXslCopyOf(string $select): Element
90	{
91		$element = $this->createElementXSL('copy-of');
92		$element->setAttribute('select', $select);
93
94		return $element;
95	}
96
97	/**
98	* Create and return an xsl:if element
99	*
100	* @param  string  $test XPath expression for the "test" attribute
101	* @param  string  $text Text content for the element
102	* @return Element
103	*/
104	public function createXslIf(string $test, string $text = ''): Element
105	{
106		$element = $this->createElementXSL('if', $text);
107		$element->setAttribute('test', $test);
108
109		return $element;
110	}
111
112	/**
113	* Create and return an xsl:otherwise element
114	*
115	* @param  string  $text Text content for the element
116	* @return Element
117	*/
118	public function createXslOtherwise(string $text = ''): Element
119	{
120		return $this->createElementXSL('otherwise', $text);
121	}
122
123	/**
124	* Create and return an xsl:text element
125	*
126	* @param  string  $text Text content for the element
127	* @return Element
128	*/
129	public function createXslText(string $text = ''): Element
130	{
131		return $this->createElementXSL('text', $text);
132	}
133
134	/**
135	* Create and return an xsl:value-of element
136	*
137	* @param  string  $select XPath expression for the "select" attribute
138	* @return Element
139	*/
140	public function createXslValueOf(string $select): Element
141	{
142		$element = $this->createElementXSL('value-of');
143		$element->setAttribute('select', $select);
144
145		return $element;
146	}
147
148	/**
149	* Create and return an xsl:variable element
150	*
151	* @param  string  $name   Name of the variable
152	* @param  string  $select XPath expression
153	* @return Element
154	*/
155	public function createXslVariable(string $name, string $select = null): Element
156	{
157		$element = $this->createElementXSL('variable');
158		$element->setAttribute('name', $name);
159		if (isset($select))
160		{
161			$element->setAttribute('select', $select);
162		}
163
164		return $element;
165	}
166
167	/**
168	* Create and return an xsl:when element
169	*
170	* @param  string  $test XPath expression for the "test" attribute
171	* @param  string  $text Text content for the element
172	* @return Element
173	*/
174	public function createXslWhen(string $test, string $text = ''): Element
175	{
176		$element = $this->createElementXSL('when', $text);
177		$element->setAttribute('test', $test);
178
179		return $element;
180	}
181
182	/**
183	* Evaluate and return the result of a given XPath expression
184	*
185	* @param  string  $expr           XPath expression
186	* @param  DOMNode $node           Context node
187	* @param  bool    $registerNodeNS Whether to register the node's namespace
188	* @return mixed
189	*/
190	public function evaluate(string $expr, DOMNode $node = null, bool $registerNodeNS = true)
191	{
192		return $this->xpath('evaluate', func_get_args());
193	}
194
195	/**
196	* Evaluate and return the first element of a given XPath query
197	*
198	* @param  string      $expr           XPath expression
199	* @param  DOMNode     $node           Context node
200	* @param  bool        $registerNodeNS Whether to register the node's namespace
201	* @return DOMNode|null
202	*/
203	public function firstOf(string $expr, DOMNode $node = null, bool $registerNodeNS = true): ?DOMNode
204	{
205		return $this->xpath('query', func_get_args())->item(0);
206	}
207
208	/**
209	* Evaluate and return the result of a given XPath query
210	*
211	* @param  string      $expr           XPath expression
212	* @param  DOMNode     $node           Context node
213	* @param  bool        $registerNodeNS Whether to register the node's namespace
214	* @return DOMNodeList
215	*/
216	public function query(string $expr, DOMNode $node = null, bool $registerNodeNS = true): DOMNodeList
217	{
218		return $this->xpath('query', func_get_args());
219	}
220
221	/**
222	* Create and return an XSL element
223	*
224	* @param  string  $name Element's local name
225	* @param  string  $text Text content for the element
226	* @return Element
227	*/
228	protected function createElementXSL(string $localName, string $text = ''): Element
229	{
230		return $this->createElementNS(
231			'http://www.w3.org/1999/XSL/Transform',
232			'xsl:' . $localName,
233			htmlspecialchars($text, ENT_XML1)
234		);
235	}
236
237	/**
238	* Execute a DOMXPath method and return the result
239	*
240	* @param  string $methodName
241	* @param  array  $args
242	* @return mixed
243	*/
244	protected function xpath(string $methodName, array $args)
245	{
246		$xpath = new DOMXPath($this);
247		$xpath->registerNamespace('xsl', 'http://www.w3.org/1999/XSL/Transform');
248
249		return call_user_func_array([$xpath, $methodName], $args);
250	}
251}