summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShane Caraveo <shane@php.net>2003-10-05 20:45:27 +0000
committerShane Caraveo <shane@php.net>2003-10-05 20:45:27 +0000
commit87bd7d57c64cf35456aa4b10316f353896904be2 (patch)
treef6d25f446deb60b2d44e642c50f4b48d2f4f82f6
parent31a3c871599ea66d70ce1b9fc7c24da922d3cca0 (diff)
downloadphp-git-87bd7d57c64cf35456aa4b10316f353896904be2.tar.gz
Add schema and relaxNG validation support
domdocument->schemaValidate(string filename) domdocument->schemaValidateSource(string xml) domdocument->relaxNGValidate(string filename) domdocument->relaxNGValidateSource(string xml) also fix domelement->setAttributeNS
-rw-r--r--ext/dom/document.c283
-rw-r--r--ext/dom/dom_fe.h7
-rw-r--r--ext/dom/element.c13
-rw-r--r--ext/dom/examples/relaxNG.php11
-rw-r--r--ext/dom/examples/relaxNG.rng11
-rw-r--r--ext/dom/examples/relaxNG.xml1
-rw-r--r--ext/dom/examples/relaxNG2.rng23
-rw-r--r--ext/dom/examples/relaxNG3.rng8
-rw-r--r--ext/dom/examples/shipping.php11
-rw-r--r--ext/dom/examples/shipping.xml21
-rw-r--r--ext/dom/examples/shipping.xsd36
11 files changed, 373 insertions, 52 deletions
diff --git a/ext/dom/document.c b/ext/dom/document.c
index 5b59dba232..e9e08a85db 100644
--- a/ext/dom/document.c
+++ b/ext/dom/document.c
@@ -27,6 +27,10 @@
#if HAVE_LIBXML && HAVE_DOM
#include "php_dom.h"
#include <libxml/SAX.h>
+#ifdef LIBXML_SCHEMAS_ENABLED
+#include <libxml/relaxng.h>
+#include <libxml/xmlschemas.h>
+#endif
typedef struct _idsIterator idsIterator;
struct _idsIterator {
@@ -83,6 +87,12 @@ zend_function_entry php_dom_document_class_functions[] = {
PHP_FALIAS(saveHTML, dom_document_save_html, NULL)
PHP_FALIAS(saveHTMLFile, dom_document_save_html_file, NULL)
#endif /* defined(LIBXML_HTML_ENABLED) */
+#if defined(LIBXML_SCHEMAS_ENABLED)
+ PHP_FALIAS(schemaValidate, dom_document_schema_validate_file, NULL)
+ PHP_FALIAS(schemaValidateSource, dom_document_schema_validate_xml, NULL)
+ PHP_FALIAS(relaxNGValidate, dom_document_relaxNG_validate_file, NULL)
+ PHP_FALIAS(relaxNGValidateSource, dom_document_relaxNG_validate_xml, NULL)
+#endif
{NULL, NULL, NULL}
};
@@ -1204,6 +1214,56 @@ PHP_FUNCTION(dom_document_document)
}
/* }}} end dom_document_document */
+char *_dom_get_valid_file_path(char *source, char *resolved_path, int resolved_path_len TSRMLS_DC) {
+ xmlURI *uri;
+ xmlChar *escsource;
+ char *file_dest;
+ int isFileUri = 0;
+
+ uri = xmlCreateURI();
+ escsource = xmlURIEscapeStr(source, ":");
+ xmlParseURIReference(uri, escsource);
+ xmlFree(escsource);
+
+ if (uri->scheme != NULL) {
+ /* absolute file uris - libxml only supports localhost or empty host */
+ if (strncasecmp(source, "file:///",8) == 0) {
+ isFileUri = 1;
+#ifdef PHP_WIN32
+ source += 8;
+#else
+ source += 7;
+#endif
+ } else if (strncasecmp(source, "file://localhost/",17) == 0) {
+ isFileUri = 1;
+#ifdef PHP_WIN32
+ source += 17;
+#else
+ source += 16;
+#endif
+ }
+ }
+
+ file_dest = source;
+
+ if ((uri->scheme == NULL || isFileUri)) {
+ /* XXX possible buffer overflow if VCWD_REALPATH does not know size of resolved_path */
+ if (! VCWD_REALPATH(source, resolved_path)) {
+ expand_filepath(source, resolved_path TSRMLS_CC);
+ }
+ file_dest = resolved_path;
+ }
+
+ xmlFreeURI(uri);
+
+ if ((PG(safe_mode) && (!php_checkuid(file_dest, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(file_dest TSRMLS_CC)) {
+ return NULL;
+ } else {
+ return file_dest;
+ }
+}
+
+
/* {{{ */
static xmlDocPtr dom_document_parser(zval *id, int mode, char *source TSRMLS_DC) {
xmlDocPtr ret;
@@ -1235,50 +1295,8 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source TSRMLS_DC)
keep_blanks = xmlKeepBlanksDefault(keep_blanks);
if (mode == DOM_LOAD_FILE) {
-
- xmlURI *uri;
- xmlChar *escsource;
- char *file_dest;
- int isFileUri = 0;
-
- uri = xmlCreateURI();
- escsource = xmlURIEscapeStr(source, ":");
- xmlParseURIReference(uri, escsource);
- xmlFree(escsource);
-
- if (uri->scheme != NULL) {
- /* absolute file uris - libxml only supports localhost or empty host */
- if (strncasecmp(source, "file:///",8) == 0) {
- isFileUri = 1;
-#ifdef PHP_WIN32
- source += 8;
-#else
- source += 7;
-#endif
- } else if (strncasecmp(source, "file://localhost/",17) == 0) {
- isFileUri = 1;
-#ifdef PHP_WIN32
- source += 17;
-#else
- source += 16;
-#endif
- }
- }
-
- file_dest = source;
-
- if ((uri->scheme == NULL || isFileUri)) {
- if (! VCWD_REALPATH(source, resolved_path)) {
- expand_filepath(source, resolved_path TSRMLS_CC);
- }
- file_dest = resolved_path;
- }
-
- xmlFreeURI(uri);
-
- if ((PG(safe_mode) && (!php_checkuid(file_dest, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(file_dest TSRMLS_CC)) {
- ctxt = NULL;
- } else {
+ char *file_dest = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC);
+ if (file_dest) {
ctxt = xmlCreateFileParserCtxt(file_dest);
}
} else {
@@ -1293,11 +1311,11 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source TSRMLS_DC)
/* If loading from memory, we need to set the base directory for the document */
if (mode != DOM_LOAD_FILE) {
- #if HAVE_GETCWD
- directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
- #elif HAVE_GETWD
- directory = VCWD_GETWD(resolved_path);
- #endif
+#if HAVE_GETCWD
+ directory = VCWD_GETCWD(resolved_path, MAXPATHLEN);
+#elif HAVE_GETWD
+ directory = VCWD_GETWD(resolved_path);
+#endif
if (directory) {
if(ctxt->directory != NULL) {
xmlFree((char *) ctxt->directory);
@@ -1531,6 +1549,173 @@ PHP_FUNCTION(dom_document_validate)
}
/* }}} end dom_document_validate */
+#if defined(LIBXML_SCHEMAS_ENABLED)
+static void
+_dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type)
+{
+ zval *id;
+ xmlDoc *docp;
+ dom_object *intern;
+ char *source = NULL, *valid_file = NULL, *directory = NULL;
+ int source_len = 0;
+ xmlSchemaParserCtxtPtr parser;
+ xmlSchemaPtr sptr;
+ xmlSchemaValidCtxtPtr vptr;
+ int is_valid;
+ char resolved_path[MAXPATHLEN + 1];
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) {
+ return;
+ }
+
+ DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern);
+
+ switch (type) {
+ case DOM_LOAD_FILE:
+ valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC);
+ if (!valid_file) {
+ php_error(E_WARNING, "Invalid Schema file source");
+ RETURN_FALSE;
+ }
+ parser = xmlSchemaNewParserCtxt(valid_file);
+ break;
+ case DOM_LOAD_STRING:
+ parser = xmlSchemaNewMemParserCtxt(source, source_len);
+ /* If loading from memory, we need to set the base directory for the document
+ but it is not apparent how to do that for schema's */
+ break;
+ }
+
+ xmlSchemaSetParserErrors(parser,
+ (xmlSchemaValidityErrorFunc) php_dom_validate_error,
+ (xmlSchemaValidityWarningFunc) php_dom_validate_error,
+ parser);
+ sptr = xmlSchemaParse(parser);
+ xmlSchemaFreeParserCtxt(parser);
+ if (!sptr) {
+ php_error(E_WARNING, "Invalid Schema");
+ RETURN_FALSE;
+ }
+
+ docp = (xmlDocPtr) dom_object_get_node(intern);
+
+ vptr = xmlSchemaNewValidCtxt(sptr);
+ if (!vptr) {
+ xmlSchemaFree(sptr);
+ php_error(E_ERROR, "Invalid Schema Validation Context");
+ RETURN_FALSE;
+ }
+
+ xmlSchemaSetValidErrors(vptr, php_dom_validate_error, php_dom_validate_error, vptr);
+ is_valid = xmlSchemaValidateDoc(vptr, docp);
+ xmlSchemaFree(sptr);
+ xmlSchemaFreeValidCtxt(vptr);
+
+ if (is_valid == 0) {
+ RETURN_TRUE;
+ } else {
+ RETURN_FALSE;
+ }
+}
+
+/* {{{ proto boolean domnode _dom_document_schema_validate(string filename); */
+PHP_FUNCTION(dom_document_schema_validate_file)
+{
+ _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
+}
+/* }}} end _dom_document_schema_validate */
+
+/* {{{ proto boolean domnode _dom_document_schema_validate(string source); */
+PHP_FUNCTION(dom_document_schema_validate_xml)
+{
+ _dom_document_schema_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
+}
+/* }}} end _dom_document_schema_validate */
+
+
+static void
+_dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int type)
+{
+ zval *id;
+ xmlDoc *docp;
+ dom_object *intern;
+ char *source = NULL, *valid_file = NULL, *directory = NULL;
+ int source_len = 0;
+ xmlRelaxNGParserCtxtPtr parser;
+ xmlRelaxNGPtr sptr;
+ xmlRelaxNGValidCtxtPtr vptr;
+ int is_valid;
+ char resolved_path[MAXPATHLEN + 1];
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &source, &source_len) == FAILURE) {
+ return;
+ }
+
+ DOM_GET_THIS_OBJ(docp, id, xmlDocPtr, intern);
+
+ switch (type) {
+ case DOM_LOAD_FILE:
+ valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN TSRMLS_CC);
+ if (!valid_file) {
+ php_error(E_WARNING, "Invalid RelaxNG file source");
+ RETURN_FALSE;
+ }
+ parser = xmlRelaxNGNewParserCtxt(valid_file);
+ break;
+ case DOM_LOAD_STRING:
+ parser = xmlRelaxNGNewMemParserCtxt(source, source_len);
+ /* If loading from memory, we need to set the base directory for the document
+ but it is not apparent how to do that for schema's */
+ break;
+ }
+
+ xmlRelaxNGSetParserErrors(parser,
+ (xmlRelaxNGValidityErrorFunc) php_dom_validate_error,
+ (xmlRelaxNGValidityWarningFunc) php_dom_validate_error,
+ parser);
+ sptr = xmlRelaxNGParse(parser);
+ xmlRelaxNGFreeParserCtxt(parser);
+ if (!sptr) {
+ php_error(E_WARNING, "Invalid RelaxNG");
+ RETURN_FALSE;
+ }
+
+ docp = (xmlDocPtr) dom_object_get_node(intern);
+
+ vptr = xmlRelaxNGNewValidCtxt(sptr);
+ if (!vptr) {
+ xmlRelaxNGFree(sptr);
+ php_error(E_ERROR, "Invalid RelaxNG Validation Context");
+ RETURN_FALSE;
+ }
+
+ xmlRelaxNGSetValidErrors(vptr, php_dom_validate_error, php_dom_validate_error, vptr);
+ is_valid = xmlRelaxNGValidateDoc(vptr, docp);
+ xmlRelaxNGFree(sptr);
+ xmlRelaxNGFreeValidCtxt(vptr);
+
+ if (is_valid == 0) {
+ RETURN_TRUE;
+ } else {
+ RETURN_FALSE;
+ }
+}
+
+/* {{{ proto boolean domnode dom_document_relaxNG_validate_file(string filename); */
+PHP_FUNCTION(dom_document_relaxNG_validate_file)
+{
+ _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_FILE);
+}
+/* }}} end dom_document_relaxNG_validate_file */
+
+/* {{{ proto boolean domnode dom_document_relaxNG_validate_xml(string source); */
+PHP_FUNCTION(dom_document_relaxNG_validate_xml)
+{
+ _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAM_PASSTHRU, DOM_LOAD_STRING);
+}
+/* }}} end dom_document_relaxNG_validate_xml */
+
+#endif
#if defined(LIBXML_HTML_ENABLED)
diff --git a/ext/dom/dom_fe.h b/ext/dom/dom_fe.h
index b8406bee98..c5a8b450c3 100644
--- a/ext/dom/dom_fe.h
+++ b/ext/dom/dom_fe.h
@@ -134,6 +134,13 @@ PHP_FUNCTION(dom_document_save_html);
PHP_FUNCTION(dom_document_save_html_file);
#endif /* defined(LIBXML_HTML_ENABLED) */
+#if defined(LIBXML_SCHEMAS_ENABLED)
+PHP_FUNCTION(dom_document_schema_validate_file);
+PHP_FUNCTION(dom_document_schema_validate_xml);
+PHP_FUNCTION(dom_document_relaxNG_validate_file);
+PHP_FUNCTION(dom_document_relaxNG_validate_xml);
+#endif
+
/* domnode methods */
PHP_FUNCTION(dom_node_insert_before);
PHP_FUNCTION(dom_node_replace_child);
diff --git a/ext/dom/element.c b/ext/dom/element.c
index ceffc972f2..063b24e6d5 100644
--- a/ext/dom/element.c
+++ b/ext/dom/element.c
@@ -497,15 +497,16 @@ PHP_FUNCTION(dom_element_set_attribute_ns)
zval *id, *rv = NULL;
xmlNodePtr elemp, nodep = NULL;
xmlNsPtr nsptr;
- int ret, uri_len = 0, name_len = 0;
- char *uri, *name;
+ xmlAttr *attr;
+ int ret, uri_len = 0, name_len = 0, value_len = 0;
+ char *uri, *name, *value;
char *localname = NULL, *prefix = NULL;
dom_object *intern;
int errorcode = 0, stricterror;
DOM_GET_THIS_OBJ(elemp, id, xmlNodePtr, intern);
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &uri, &uri_len, &name, &name_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss", &uri, &uri_len, &name, &name_len, &value, &value_len) == FAILURE) {
return;
}
@@ -538,6 +539,12 @@ PHP_FUNCTION(dom_element_set_attribute_ns)
if (errorcode == 0) {
nodep = (xmlNodePtr) xmlSetNsProp(elemp, nsptr, localname, NULL);
}
+
+ attr = xmlSetProp(nodep, localname, value);
+ if (!attr) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "No such attribute '%s'", localname);
+ RETURN_FALSE;
+ }
}
xmlFree(localname);
diff --git a/ext/dom/examples/relaxNG.php b/ext/dom/examples/relaxNG.php
new file mode 100644
index 0000000000..d265fd988e
--- /dev/null
+++ b/ext/dom/examples/relaxNG.php
@@ -0,0 +1,11 @@
+<?php
+
+$dom = new domDocument;
+$dom->load('relaxNG.xml');
+if (!$dom->relaxNGValidate('relaxNG.rng')) {
+ print "Document is not valid";
+} else {
+ print "Document is valid";
+}
+
+?> \ No newline at end of file
diff --git a/ext/dom/examples/relaxNG.rng b/ext/dom/examples/relaxNG.rng
new file mode 100644
index 0000000000..f4357e04ef
--- /dev/null
+++ b/ext/dom/examples/relaxNG.rng
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+ datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+<include href="relaxNG2.rng">
+<define name="TEI.prose"><ref name="INCLUDE"/></define>
+</include>
+</grammar>
+
+
+
diff --git a/ext/dom/examples/relaxNG.xml b/ext/dom/examples/relaxNG.xml
new file mode 100644
index 0000000000..6b0cac1225
--- /dev/null
+++ b/ext/dom/examples/relaxNG.xml
@@ -0,0 +1 @@
+<TEI.2>hello</TEI.2> \ No newline at end of file
diff --git a/ext/dom/examples/relaxNG2.rng b/ext/dom/examples/relaxNG2.rng
new file mode 100644
index 0000000000..4adae7b151
--- /dev/null
+++ b/ext/dom/examples/relaxNG2.rng
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:t="http://www.thaiopensource.com/ns/annotations" xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <start>
+ <ref name="TEI.2"/>
+ </start>
+ <define name="IGNORE">
+ <notAllowed/>
+ </define>
+ <define name="INCLUDE">
+ <empty/>
+ </define>
+
+
+ <include href="relaxNG3.rng"/>
+
+ <define name="TEI.2">
+ <element name="TEI.2">
+ <text/>
+ </element>
+ </define>
+
+</grammar> \ No newline at end of file
diff --git a/ext/dom/examples/relaxNG3.rng b/ext/dom/examples/relaxNG3.rng
new file mode 100644
index 0000000000..73e1eb6165
--- /dev/null
+++ b/ext/dom/examples/relaxNG3.rng
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:t="http://www.thaiopensource.com/ns/annotations" xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <define name="TEI.prose" combine="interleave">
+ <ref name="IGNORE"/>
+ </define>
+
+</grammar> \ No newline at end of file
diff --git a/ext/dom/examples/shipping.php b/ext/dom/examples/shipping.php
new file mode 100644
index 0000000000..5205fd2014
--- /dev/null
+++ b/ext/dom/examples/shipping.php
@@ -0,0 +1,11 @@
+<?php
+
+$dom = new domDocument;
+$dom->load('shipping.xml');
+if (!$dom->schemaValidate('shipping.xsd')) {
+ print "Document is not valid";
+} else {
+ print "Document is valid";
+}
+
+?> \ No newline at end of file
diff --git a/ext/dom/examples/shipping.xml b/ext/dom/examples/shipping.xml
new file mode 100644
index 0000000000..dc8a09e301
--- /dev/null
+++ b/ext/dom/examples/shipping.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<shipOrder>
+ <shipTo>
+ <name>Tove Svendson</name>
+ <street>Ragnhildvei 2</street>
+ <address>4000 Stavanger</address>
+ <country>Norway</country>
+ </shipTo>
+ <items>
+ <item>
+ <title>Empire Burlesque</title>
+ <quantity>1</quantity>
+ <price>10.90</price>
+ </item>
+ <item>
+ <title>Hide your heart</title>
+ <quantity>1</quantity>
+ <price>9.90</price>
+ </item>
+ </items>
+</shipOrder> \ No newline at end of file
diff --git a/ext/dom/examples/shipping.xsd b/ext/dom/examples/shipping.xsd
new file mode 100644
index 0000000000..8b16b7c03a
--- /dev/null
+++ b/ext/dom/examples/shipping.xsd
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <xsd:element name="shipOrder" type="order"/>
+
+ <xsd:complexType name="order">
+ <xsd:all>
+ <xsd:element name="shipTo" type="shipAddress"/>
+ <xsd:element name="items" type="cdItems"/>
+ </xsd:all>
+ </xsd:complexType>
+
+ <xsd:complexType name="shipAddress">
+ <xsd:all>
+ <xsd:element name="name" type="xsd:string"/>
+ <xsd:element name="street" type="xsd:string"/>
+ <xsd:element name="address" type="xsd:string"/>
+ <xsd:element name="country" type="xsd:string"/>
+ </xsd:all>
+ </xsd:complexType>
+
+ <xsd:complexType name="cdItems">
+ <xsd:sequence>
+ <xsd:element name="item" type="cdItem" maxOccurs="unbounded" minOccurs="1"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="cdItem">
+ <xsd:all>
+ <xsd:element name="title" type="xsd:string"/>
+ <xsd:element name="quantity" type="xsd:positiveInteger"/>
+ <xsd:element name="price" type="xsd:decimal"/>
+ </xsd:all>
+ </xsd:complexType>
+
+</xsd:schema> \ No newline at end of file