]> granicus.if.org Git - php/commitdiff
Add schema and relaxNG validation support
authorShane Caraveo <shane@php.net>
Sun, 5 Oct 2003 20:45:27 +0000 (20:45 +0000)
committerShane Caraveo <shane@php.net>
Sun, 5 Oct 2003 20:45:27 +0000 (20:45 +0000)
domdocument->schemaValidate(string filename)
domdocument->schemaValidateSource(string xml)
domdocument->relaxNGValidate(string filename)
domdocument->relaxNGValidateSource(string xml)

also fix domelement->setAttributeNS

ext/dom/document.c
ext/dom/dom_fe.h
ext/dom/element.c
ext/dom/examples/relaxNG.php [new file with mode: 0644]
ext/dom/examples/relaxNG.rng [new file with mode: 0644]
ext/dom/examples/relaxNG.xml [new file with mode: 0644]
ext/dom/examples/relaxNG2.rng [new file with mode: 0644]
ext/dom/examples/relaxNG3.rng [new file with mode: 0644]
ext/dom/examples/shipping.php [new file with mode: 0644]
ext/dom/examples/shipping.xml [new file with mode: 0644]
ext/dom/examples/shipping.xsd [new file with mode: 0644]

index 5b59dba2320415bbaa8471168bd15f3f02e235d6..e9e08a85db2e5daa62d150b65bb90f2cac09d539 100644 (file)
 #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)
 
index b8406bee985819df3cf909459d37c5955878ada0..c5a8b450c3bae9c72e76be911536633024a8d65a 100644 (file)
@@ -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);
index ceffc972f28ef1886b74c78544090756afff1f96..063b24e6d56343cdc66bac4ea20ff1805ee9856a 100644 (file)
@@ -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 (file)
index 0000000..d265fd9
--- /dev/null
@@ -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 (file)
index 0000000..f4357e0
--- /dev/null
@@ -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 (file)
index 0000000..6b0cac1
--- /dev/null
@@ -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 (file)
index 0000000..4adae7b
--- /dev/null
@@ -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 (file)
index 0000000..73e1eb6
--- /dev/null
@@ -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 (file)
index 0000000..5205fd2
--- /dev/null
@@ -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 (file)
index 0000000..dc8a09e
--- /dev/null
@@ -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 (file)
index 0000000..8b16b7c
--- /dev/null
@@ -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