diff options
author | Shane Caraveo <shane@php.net> | 2003-10-05 20:45:27 +0000 |
---|---|---|
committer | Shane Caraveo <shane@php.net> | 2003-10-05 20:45:27 +0000 |
commit | 87bd7d57c64cf35456aa4b10316f353896904be2 (patch) | |
tree | f6d25f446deb60b2d44e642c50f4b48d2f4f82f6 | |
parent | 31a3c871599ea66d70ce1b9fc7c24da922d3cca0 (diff) | |
download | php-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.c | 283 | ||||
-rw-r--r-- | ext/dom/dom_fe.h | 7 | ||||
-rw-r--r-- | ext/dom/element.c | 13 | ||||
-rw-r--r-- | ext/dom/examples/relaxNG.php | 11 | ||||
-rw-r--r-- | ext/dom/examples/relaxNG.rng | 11 | ||||
-rw-r--r-- | ext/dom/examples/relaxNG.xml | 1 | ||||
-rw-r--r-- | ext/dom/examples/relaxNG2.rng | 23 | ||||
-rw-r--r-- | ext/dom/examples/relaxNG3.rng | 8 | ||||
-rw-r--r-- | ext/dom/examples/shipping.php | 11 | ||||
-rw-r--r-- | ext/dom/examples/shipping.xml | 21 | ||||
-rw-r--r-- | ext/dom/examples/shipping.xsd | 36 |
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 |