1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Christian Stocker <chregu@php.net> |
14 | Rob Richards <rrichards@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "php.h"
23 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
24 #include "php_dom.h"
25 #include "namespace_compat.h"
26
27 /*
28 * class DOMImplementation
29 *
30 * URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-102161490
31 * Since:
32 */
33
34 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#ID-5CED94D7
35 Since:
36 */
PHP_METHOD(DOMImplementation,hasFeature)37 PHP_METHOD(DOMImplementation, hasFeature)
38 {
39 zend_string *feature, *version;
40
41 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &feature, &version) == FAILURE) {
42 RETURN_THROWS();
43 }
44
45 RETURN_BOOL(dom_has_feature(feature, version));
46 }
47 /* }}} end dom_domimplementation_has_feature */
48
49 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocType
50 Modern URL: https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype
51 Since: DOM Level 2
52 */
PHP_METHOD(DOMImplementation,createDocumentType)53 PHP_METHOD(DOMImplementation, createDocumentType)
54 {
55 xmlDtd *doctype;
56 size_t name_len = 0, publicid_len = 0, systemid_len = 0;
57 char *name = NULL, *publicid = NULL, *systemid = NULL;
58 xmlChar *pch1 = NULL, *pch2 = NULL, *localname = NULL;
59 xmlURIPtr uri;
60
61 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|ss", &name, &name_len, &publicid, &publicid_len, &systemid, &systemid_len) == FAILURE) {
62 RETURN_THROWS();
63 }
64
65 if (name_len == 0) {
66 zend_argument_value_error(1, "cannot be empty");
67 RETURN_THROWS();
68 }
69
70 if (publicid_len > 0) {
71 pch1 = BAD_CAST publicid;
72 }
73 if (systemid_len > 0) {
74 pch2 = BAD_CAST systemid;
75 }
76
77 if (strstr(name, "%00")) {
78 php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
79 RETURN_FALSE;
80 }
81
82 uri = xmlParseURI(name);
83 if (uri != NULL && uri->opaque != NULL) {
84 localname = xmlStrdup(BAD_CAST uri->opaque);
85 if (xmlStrchr(localname, (xmlChar) ':') != NULL) {
86 php_dom_throw_error(NAMESPACE_ERR, true);
87 xmlFreeURI(uri);
88 xmlFree(localname);
89 RETURN_FALSE;
90 }
91 } else {
92 localname = xmlStrdup(BAD_CAST name);
93 }
94
95 if (uri) {
96 xmlFreeURI(uri);
97 }
98
99 doctype = xmlCreateIntSubset(NULL, localname, pch1, pch2);
100 xmlFree(localname);
101
102 if (doctype == NULL) {
103 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
104 RETURN_THROWS();
105 }
106
107 DOM_RET_OBJ((xmlNodePtr) doctype, NULL);
108 }
109
PHP_METHOD(Dom_Implementation,createDocumentType)110 PHP_METHOD(Dom_Implementation, createDocumentType)
111 {
112 size_t name_len, publicid_len = 0, systemid_len = 0;
113 const char *name, *publicid = NULL, *systemid = NULL;
114
115 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppp", &name, &name_len, &publicid, &publicid_len, &systemid, &systemid_len) != SUCCESS) {
116 RETURN_THROWS();
117 }
118
119 /* 1. Validate qualifiedName. */
120 if (xmlValidateQName(BAD_CAST name, 0) != 0) {
121 php_dom_throw_error(NAMESPACE_ERR, true);
122 RETURN_THROWS();
123 }
124
125 /* 2. Return a new doctype, with qualifiedName as its name, publicId as its public ID, and systemId as its system ID ... */
126 xmlDtdPtr doctype = xmlCreateIntSubset(
127 NULL,
128 BAD_CAST name,
129 publicid_len ? BAD_CAST publicid : NULL,
130 systemid_len ? BAD_CAST systemid : NULL
131 );
132 if (UNEXPECTED(doctype == NULL)) {
133 php_dom_throw_error(INVALID_STATE_ERR, true);
134 RETURN_THROWS();
135 }
136
137 php_dom_instantiate_object_helper(
138 return_value,
139 dom_modern_documenttype_class_entry,
140 (xmlNodePtr) doctype,
141 NULL
142 );
143 }
144 /* }}} end dom_domimplementation_create_document_type */
145
146 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Level-2-Core-DOM-createDocument
147 Since: DOM Level 2
148 */
PHP_METHOD(DOMImplementation,createDocument)149 PHP_METHOD(DOMImplementation, createDocument)
150 {
151 zval *node = NULL;
152 xmlDoc *docp;
153 xmlNode *nodep;
154 xmlDtdPtr doctype = NULL;
155 xmlNsPtr nsptr = NULL;
156 int errorcode = 0;
157 size_t uri_len = 0, name_len = 0;
158 char *uri = NULL, *name = NULL;
159 char *prefix = NULL, *localname = NULL;
160 dom_object *doctobj;
161
162 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!sO!", &uri, &uri_len, &name, &name_len, &node, dom_documenttype_class_entry) == FAILURE) {
163 RETURN_THROWS();
164 }
165
166 if (node != NULL) {
167 DOM_GET_OBJ(doctype, node, xmlDtdPtr, doctobj);
168 if (doctype->type == XML_DOCUMENT_TYPE_NODE) {
169 zend_argument_value_error(3, "is an invalid DocumentType object");
170 RETURN_THROWS();
171 }
172 if (doctype->doc != NULL) {
173 /* As the new document is the context node, and the default for strict error checking
174 * is true, this will always throw. */
175 php_dom_throw_error(WRONG_DOCUMENT_ERR, true);
176 RETURN_THROWS();
177 }
178 } else {
179 doctobj = NULL;
180 }
181
182 if (name_len > 0) {
183 errorcode = dom_check_qname(name, &localname, &prefix, 1, name_len);
184 if (errorcode == 0 && uri_len > 0
185 && ((nsptr = xmlNewNs(NULL, BAD_CAST uri, BAD_CAST prefix)) == NULL)
186 ) {
187 errorcode = NAMESPACE_ERR;
188 }
189 }
190
191 if (prefix != NULL) {
192 xmlFree(prefix);
193 }
194
195 if (errorcode != 0) {
196 if (localname != NULL) {
197 xmlFree(localname);
198 }
199 php_dom_throw_error(errorcode, true);
200 RETURN_THROWS();
201 }
202
203 /* currently letting libxml2 set the version string */
204 docp = xmlNewDoc(NULL);
205 if (!docp) {
206 if (localname != NULL) {
207 xmlFree(localname);
208 }
209 /* See above for strict error checking argument. */
210 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
211 RETURN_THROWS();
212 }
213
214 if (doctype != NULL) {
215 docp->intSubset = doctype;
216 doctype->parent = docp;
217 doctype->doc = docp;
218 docp->children = (xmlNodePtr) doctype;
219 docp->last = (xmlNodePtr) doctype;
220 }
221
222 if (localname != NULL) {
223 nodep = xmlNewDocNode(docp, nsptr, BAD_CAST localname, NULL);
224 if (!nodep) {
225 if (doctype != NULL) {
226 docp->intSubset = NULL;
227 doctype->parent = NULL;
228 doctype->doc = NULL;
229 docp->children = NULL;
230 docp->last = NULL;
231 }
232 xmlFreeDoc(docp);
233 xmlFree(localname);
234 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
235 RETURN_THROWS();
236 }
237
238 nodep->nsDef = nsptr;
239
240 xmlDocSetRootElement(docp, nodep);
241 xmlFree(localname);
242 }
243
244 DOM_RET_OBJ((xmlNodePtr) docp, NULL);
245
246 if (doctobj != NULL) {
247 doctobj->document = ((dom_object *)((php_libxml_node_ptr *)docp->_private)->_private)->document;
248 php_libxml_increment_doc_ref((php_libxml_node_object *)doctobj, docp);
249 }
250 }
251
PHP_METHOD(Dom_Implementation,createDocument)252 PHP_METHOD(Dom_Implementation, createDocument)
253 {
254 zval *dtd = NULL;
255 xmlDtdPtr doctype = NULL;
256 zend_string *uri = NULL, *qualified_name = zend_empty_string;
257 dom_object *doctobj;
258
259 if (zend_parse_parameters(ZEND_NUM_ARGS(), "P!P|O!", &uri, &qualified_name, &dtd, dom_modern_documenttype_class_entry) != SUCCESS) {
260 RETURN_THROWS();
261 }
262
263 if (dtd != NULL) {
264 DOM_GET_OBJ(doctype, dtd, xmlDtdPtr, doctobj);
265 }
266
267 xmlDocPtr document = NULL;
268 xmlChar *localname = NULL, *prefix = NULL;
269 php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create();
270
271 /* 1. Let document be a new XMLDocument. */
272 document = xmlNewDoc(BAD_CAST "1.0");
273 if (UNEXPECTED(document == NULL)) {
274 goto oom;
275 }
276 document->encoding = xmlStrdup(BAD_CAST "UTF-8");
277
278 /* 2. Let element be null. */
279 xmlNodePtr element = NULL;
280
281 /* 3. If qualifiedName is not the empty string, then set element to the result of running the internal createElementNS steps. */
282 if (ZSTR_LEN(qualified_name) != 0) {
283 int errorcode = dom_validate_and_extract(uri, qualified_name, &localname, &prefix);
284
285 if (EXPECTED(errorcode == 0)) {
286 xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri);
287 element = xmlNewDocNode(document, ns, localname, NULL);
288 if (UNEXPECTED(element == NULL)) {
289 goto oom;
290 }
291 xmlFree(localname);
292 xmlFree(prefix);
293 localname = NULL;
294 prefix = NULL;
295 } else {
296 php_dom_throw_error(errorcode, /* strict */ true);
297 goto error;
298 }
299 }
300
301 /* 8. Return document.
302 * => This is done here already to gain access to the dom_object */
303 dom_object *intern = php_dom_instantiate_object_helper(
304 return_value,
305 dom_xml_document_class_entry,
306 (xmlNodePtr) document,
307 NULL
308 );
309 dom_set_xml_class(intern->document);
310 intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
311
312 /* 4. If doctype is non-null, append doctype to document. */
313 if (doctype != NULL) {
314 php_dom_adopt_node((xmlNodePtr) doctype, intern, document);
315 xmlAddChild((xmlNodePtr) document, (xmlNodePtr) doctype);
316 doctype->doc = document;
317 document->intSubset = (xmlDtdPtr) doctype;
318 ZEND_ASSERT(doctype->parent == document);
319 }
320
321 /* 5. If element is non-null, append element to document. */
322 if (element != NULL) {
323 xmlAddChild((xmlNodePtr) document, element);
324 }
325
326 /* 6. document’s origin is this’s associated document’s origin.
327 * => We don't store the origin in ext/dom. */
328
329 /* 7. document’s content type is determined by namespace:
330 * => We don't store the content type in ext/dom. */
331
332 return;
333
334 oom:
335 php_dom_throw_error(INVALID_STATE_ERR, true);
336 error:
337 xmlFree(localname);
338 xmlFree(prefix);
339 xmlFreeDoc(document);
340 php_dom_libxml_ns_mapper_destroy(ns_mapper);
341 RETURN_THROWS();
342 }
343 /* }}} end dom_domimplementation_create_document */
344
345 /* {{{ URL: https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument */
PHP_METHOD(Dom_Implementation,createHTMLDocument)346 PHP_METHOD(Dom_Implementation, createHTMLDocument)
347 {
348 const char *title = NULL;
349 size_t title_len = 0;
350
351 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p!", &title, &title_len) != SUCCESS) {
352 RETURN_THROWS();
353 }
354
355 /* 1. Let doc be a new document that is an HTML document. */
356 xmlDocPtr doc = php_dom_create_html_doc();
357 if (UNEXPECTED(doc == NULL)) {
358 php_dom_throw_error(INVALID_STATE_ERR, true);
359 RETURN_THROWS();
360 }
361 doc->encoding = xmlStrdup(BAD_CAST "UTF-8");
362
363 /* 2. Set doc’s content type to "text/html".
364 * => We don't store the content type in ext/dom. */
365
366 /* 3. Append a new doctype, with "html" as its name and with its node document set to doc, to doc. */
367 xmlDtdPtr dtd = xmlCreateIntSubset(doc, BAD_CAST "html", NULL, NULL);
368
369 php_dom_libxml_ns_mapper *ns_mapper = php_dom_libxml_ns_mapper_create();
370 xmlNsPtr html_ns = php_dom_libxml_ns_mapper_ensure_html_ns(ns_mapper);
371
372 /* 4. Append the result of creating an element given doc, html, and the HTML namespace, to doc. */
373 xmlNodePtr html_element = xmlNewDocRawNode(doc, html_ns, BAD_CAST "html", NULL);
374 xmlAddChild((xmlNodePtr) doc, html_element);
375
376 /* 5. Append the result of creating an element given doc, head, and the HTML namespace, to the html element created earlier. */
377 xmlNodePtr head_element = xmlNewDocRawNode(doc, html_ns, BAD_CAST "head", NULL);
378 xmlAddChild(html_element, head_element);
379
380 /* 6. If title is given: */
381 xmlNodePtr title_element = NULL;
382 if (title != NULL) {
383 /* 6.1. Append the result of creating an element given doc, title, and the HTML namespace, to the head element created earlier. */
384 /* 6.2. Append the result of creating a text node given doc and title, to the title element created earlier. */
385 title_element = xmlNewDocRawNode(doc, html_ns, BAD_CAST "title", BAD_CAST title);
386 xmlAddChild(head_element, title_element);
387 }
388
389 /* 7. Append the result of creating an element given doc, body, and the HTML namespace, to the html element created earlier. */
390 xmlNodePtr body_element = xmlNewDocRawNode(doc, html_ns, BAD_CAST "body", NULL);
391 xmlAddChild(html_element, body_element);
392
393 /* 8. doc’s origin is this’s associated document’s origin.
394 * => We don't store the origin in ext/dom. */
395
396 if (UNEXPECTED(dtd == NULL || html_element == NULL || head_element == NULL || (title != NULL && title_element == NULL) || body_element == NULL)) {
397 php_dom_throw_error(INVALID_STATE_ERR, true);
398 xmlFreeDoc(doc);
399 php_dom_libxml_ns_mapper_destroy(ns_mapper);
400 RETURN_THROWS();
401 }
402
403 /* 9. Return doc. */
404 dom_object *intern = php_dom_instantiate_object_helper(
405 return_value,
406 dom_html_document_class_entry,
407 (xmlNodePtr) doc,
408 NULL
409 );
410 dom_set_xml_class(intern->document);
411 intern->document->private_data = php_dom_libxml_ns_mapper_header(ns_mapper);
412 }
413 /* }}} */
414
415 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#DOMImplementation3-getFeature
416 Since: DOM Level 3
417 */
PHP_METHOD(DOMImplementation,getFeature)418 PHP_METHOD(DOMImplementation, getFeature)
419 {
420 size_t feature_len, version_len;
421 char *feature, *version;
422
423 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &feature, &feature_len, &version, &version_len) == FAILURE) {
424 RETURN_THROWS();
425 }
426
427 zend_throw_error(NULL, "Not yet implemented");
428 RETURN_THROWS();
429 }
430 /* }}} end dom_domimplementation_get_feature */
431
432 #endif
433