]> granicus.if.org Git - php/commitdiff
MFH: add C14N() and C14NFile() methods to perform XML canonicalization
authorRob Richards <rrichards@php.net>
Fri, 26 May 2006 18:23:50 +0000 (18:23 +0000)
committerRob Richards <rrichards@php.net>
Fri, 26 May 2006 18:23:50 +0000 (18:23 +0000)
add test

ext/dom/dom_fe.h
ext/dom/node.c
ext/dom/tests/canonicalization.phpt [new file with mode: 0644]

index a12d2f9f77c9464590970cb08ab077dc265d6db0..111f9e25f307fac3afce405fd727246a2a9daab7 100644 (file)
@@ -165,6 +165,8 @@ PHP_FUNCTION(dom_node_is_equal_node);
 PHP_FUNCTION(dom_node_get_feature);
 PHP_FUNCTION(dom_node_set_user_data);
 PHP_FUNCTION(dom_node_get_user_data);
+PHP_METHOD(domnode, C14N);
+PHP_METHOD(domnode, C14NFile);
 
 /* domnodelist methods */
 PHP_FUNCTION(dom_nodelist_item);
index b3856adf533480b33b2840c9dfd9503f891bb058..62073ad4d978a465af833384c38c3a58f49cddd9 100644 (file)
@@ -53,6 +53,8 @@ zend_function_entry php_dom_node_class_functions[] = {
        PHP_FALIAS(getFeature, dom_node_get_feature, NULL)
        PHP_FALIAS(setUserData, dom_node_set_user_data, NULL)
        PHP_FALIAS(getUserData, dom_node_get_user_data, NULL)
+       PHP_ME(domnode, C14N, NULL, ZEND_ACC_PUBLIC)
+       PHP_ME(domnode, C14NFile, NULL, ZEND_ACC_PUBLIC)
        {NULL, NULL, NULL}
 };
 
@@ -1669,4 +1671,189 @@ PHP_FUNCTION(dom_node_get_user_data)
  DOM_NOT_IMPLEMENTED();
 }
 /* }}} end dom_node_get_user_data */
+
+
+static void dom_canonicalization(INTERNAL_FUNCTION_PARAMETERS, int mode)
+{
+       zval *id;
+       zval *xpath_array=NULL, *ns_prefixes=NULL;
+       xmlNodePtr nodep;
+       xmlDocPtr docp;
+       xmlNodeSetPtr nodeset = NULL;
+       dom_object *intern;
+       long exclusive=0, with_comments=0, file_len=0;
+       xmlChar **inclusive_ns_prefixes = NULL;
+       char *file = NULL;
+    int ret = -1;
+    xmlOutputBufferPtr buf;
+       xmlXPathContextPtr ctxp=NULL;
+       xmlXPathObjectPtr xpathobjp=NULL;
+
+       if (mode == 0) {
+               if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), 
+                       "O|bba!a!", &id, dom_node_class_entry, &exclusive, &with_comments, 
+                       &xpath_array, &ns_prefixes) == FAILURE) {
+                       return;
+               }
+       } else {
+               if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), 
+                       "Os|bba!a!", &id, dom_node_class_entry, &file, &file_len, &exclusive, 
+                       &with_comments, &xpath_array, &ns_prefixes) == FAILURE) {
+                       return;
+               }
+       }
+
+       DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
+
+       docp = nodep->doc;
+
+       if (! docp) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Node must be associated with a document");
+               RETURN_FALSE;
+       }
+
+       if (xpath_array == NULL) {
+               if (nodep->type != XML_DOCUMENT_NODE) {
+                       ctxp = xmlXPathNewContext(docp);
+                       ctxp->node = nodep;
+                       xpathobjp = xmlXPathEvalExpression("(.//. | .//@* | .//namespace::*)", ctxp);
+                       ctxp->node = NULL;
+                       if (xpathobjp && xpathobjp->type == XPATH_NODESET) {
+                               nodeset = xpathobjp->nodesetval;
+                       } else {
+                               if (xpathobjp) {
+                                       xmlXPathFreeObject(xpathobjp);
+                               }
+                               xmlXPathFreeContext(ctxp);
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "XPath query did not return a nodeset.");
+                               RETURN_FALSE;
+                       }
+               }
+       } else {
+               /*xpath query from xpath_array */
+               HashTable *ht = Z_ARRVAL_P(xpath_array);
+               zval **tmp;
+               char *xquery;
+
+               if (zend_hash_find(ht, "query", sizeof("query"), (void**)&tmp) == SUCCESS &&
+                   Z_TYPE_PP(tmp) == IS_STRING) {
+                       xquery = Z_STRVAL_PP(tmp);
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "'query' missing from xpath array or is not a string");
+                       RETURN_FALSE;
+               }
+
+               ctxp = xmlXPathNewContext(docp);
+               ctxp->node = nodep;
+
+               if (zend_hash_find(ht, "namespaces", sizeof("namespaces"), (void**)&tmp) == SUCCESS &&
+                   Z_TYPE_PP(tmp) == IS_ARRAY) {
+                       zval **tmpns;
+                       while (zend_hash_get_current_data(Z_ARRVAL_PP(tmp), (void **)&tmpns) == SUCCESS) {
+                               if (Z_TYPE_PP(tmpns) == IS_STRING) {
+                                       char *prefix;
+                                       ulong idx;
+                                       int prefix_key_len;
+
+                                       if (zend_hash_get_current_key_ex(Z_ARRVAL_PP(tmp), 
+                                               &prefix, &prefix_key_len, &idx, 0, NULL) == HASH_KEY_IS_STRING) {
+                                               xmlXPathRegisterNs(ctxp, prefix, Z_STRVAL_PP(tmpns));
+                                       }
+                               }
+                               zend_hash_move_forward(Z_ARRVAL_PP(tmp));
+                       }
+               }
+
+               xpathobjp = xmlXPathEvalExpression(xquery, ctxp);
+               ctxp->node = NULL;
+               if (xpathobjp && xpathobjp->type == XPATH_NODESET) {
+                       nodeset = xpathobjp->nodesetval;
+               } else {
+                       if (xpathobjp) {
+                               xmlXPathFreeObject(xpathobjp);
+                       }
+                       xmlXPathFreeContext(ctxp);
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "XPath query did not return a nodeset.");
+                       RETURN_FALSE;
+               }
+       }
+
+       if (ns_prefixes != NULL) {
+               if (exclusive) {
+                       zval **tmpns;
+                       int nscount = 0;
+
+                       inclusive_ns_prefixes = safe_emalloc(zend_hash_num_elements(Z_ARRVAL_P(ns_prefixes)) + 1,
+                               sizeof(xmlChar *), 0);
+                       while (zend_hash_get_current_data(Z_ARRVAL_P(ns_prefixes), (void **)&tmpns) == SUCCESS) {
+                               if (Z_TYPE_PP(tmpns) == IS_STRING) {
+                                       inclusive_ns_prefixes[nscount++] = Z_STRVAL_PP(tmpns);
+                               }
+                               zend_hash_move_forward(Z_ARRVAL_P(ns_prefixes));
+                       }
+                       inclusive_ns_prefixes[nscount] = NULL;
+               } else {
+                       php_error_docref(NULL TSRMLS_CC, E_NOTICE, 
+                               "Inclusive namespace prefixes only allowed in exlcusive mode.");
+               }
+       }
+
+       if (mode == 1) {
+               buf = xmlOutputBufferCreateFilename(file, NULL, 0);
+       } else {
+               buf = xmlAllocOutputBuffer(NULL);
+       }
+
+    if (buf != NULL) {
+               ret = xmlC14NDocSaveTo(docp, nodeset, exclusive, inclusive_ns_prefixes,
+                       with_comments, buf);
+       }
+
+       if (inclusive_ns_prefixes != NULL) {
+               efree(inclusive_ns_prefixes);
+       }
+       if (xpathobjp != NULL) {
+               xmlXPathFreeObject(xpathobjp);
+       }
+       if (ctxp != NULL) {
+               xmlXPathFreeContext(ctxp);
+       }
+
+    if (buf == NULL || ret < 0) {
+        RETVAL_FALSE;
+    } else {
+               if (mode == 0) {
+                       ret = buf->buffer->use;
+                       if (ret > 0) {
+                               RETVAL_STRINGL((char *) buf->buffer->content, ret, 1);
+                       } else {
+                               RETVAL_EMPTY_STRING();
+                       }
+               }
+    }
+
+       if (buf) {
+               int bytes;
+
+               bytes = xmlOutputBufferClose(buf);
+               if (mode == 1 && (ret >= 0)) {
+                       RETURN_LONG(bytes);
+               }
+       }
+}
+
+/* {{{ proto string DOMNode::C14N([bool exclusive [, bool with_comments [, array xpath [, array ns_prefixes]]]])
+   Canonicalize nodes to a string */
+PHP_METHOD(domnode, C14N)
+{
+       dom_canonicalization(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+
+/* {{{ proto int DOMNode::C14NFile(string uri [, bool exclusive [, bool with_comments [, array xpath [, array ns_prefixes]]]])
+   Canonicalize nodes to a file */
+PHP_METHOD(domnode, C14NFile)
+{
+       dom_canonicalization(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
+}
+
 #endif
diff --git a/ext/dom/tests/canonicalization.phpt b/ext/dom/tests/canonicalization.phpt
new file mode 100644 (file)
index 0000000..cf1a81f
--- /dev/null
@@ -0,0 +1,102 @@
+--TEST--
+Test: Canonicalization - C14N()
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--FILE--
+<?php
+$xml = <<<EOXML
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<foo xmlns="http://www.example.com/ns/foo"
+     xmlns:fubar="http://www.example.com/ns/fubar" xmlns:test="urn::test"><contain>
+  <bar><test1 /></bar>
+  <bar><test2 /></bar>
+  <fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test3 /></fubar:bar>
+  <fubar:bar><test4 /></fubar:bar>
+<!-- this is a comment -->
+</contain>
+</foo>
+EOXML;
+
+$dom = new DOMDocument();
+$dom->loadXML($xml);
+$doc = $dom->documentElement->firstChild;
+
+/* inclusive/without comments first child element of doc element is context. */
+echo $doc->C14N()."\n\n";
+
+/* exclusive/without comments first child element of doc element is context. */
+echo $doc->c14N(TRUE)."\n\n";
+
+/* inclusive/with comments first child element of doc element is context. */
+echo $doc->C14N(FALSE, TRUE)."\n\n";
+
+/* exclusive/with comments first child element of doc element is context. */
+echo $doc->C14N(TRUE, TRUE)."\n\n";
+
+/* exclusive/without comments using xpath query. */
+echo $doc->c14N(TRUE, FALSE, array('query'=>'(//. | //@* | //namespace::*)'))."\n\n";
+
+/* exclusive/without comments first child element of doc element is context.
+   using xpath query with registered namespace.
+   test namespace prefix is also included. */
+echo $doc->c14N(TRUE, FALSE, 
+                array('query'=>'(//a:contain | //a:bar | .//namespace::*)', 
+                      'namespaces'=>array('a'=>'http://www.example.com/ns/foo')), 
+                array('test'))."\n\n";
+
+/* exclusive/without comments first child element of doc element is context. 
+   test namespace prefix is also included */
+echo $doc->C14N(TRUE, FALSE, NULL, array('test'));
+?>
+--EXPECTF--
+
+<contain xmlns="http://www.example.com/ns/foo" xmlns:fubar="http://www.example.com/ns/fubar" xmlns:test="urn::test">
+  <bar><test1></test1></bar>
+  <bar><test2></test2></bar>
+  <fubar:bar><test3></test3></fubar:bar>
+  <fubar:bar><test4></test4></fubar:bar>
+
+</contain>
+
+<contain xmlns="http://www.example.com/ns/foo">
+  <bar><test1></test1></bar>
+  <bar><test2></test2></bar>
+  <fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test3></test3></fubar:bar>
+  <fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test4></test4></fubar:bar>
+
+</contain>
+
+<contain xmlns="http://www.example.com/ns/foo" xmlns:fubar="http://www.example.com/ns/fubar" xmlns:test="urn::test">
+  <bar><test1></test1></bar>
+  <bar><test2></test2></bar>
+  <fubar:bar><test3></test3></fubar:bar>
+  <fubar:bar><test4></test4></fubar:bar>
+<!-- this is a comment -->
+</contain>
+
+<contain xmlns="http://www.example.com/ns/foo">
+  <bar><test1></test1></bar>
+  <bar><test2></test2></bar>
+  <fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test3></test3></fubar:bar>
+  <fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test4></test4></fubar:bar>
+<!-- this is a comment -->
+</contain>
+
+<foo xmlns="http://www.example.com/ns/foo"><contain>
+  <bar><test1></test1></bar>
+  <bar><test2></test2></bar>
+  <fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test3></test3></fubar:bar>
+  <fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test4></test4></fubar:bar>
+
+</contain>
+</foo>
+
+<contain xmlns="http://www.example.com/ns/foo" xmlns:test="urn::test"><bar></bar><bar></bar></contain>
+
+<contain xmlns="http://www.example.com/ns/foo" xmlns:test="urn::test">
+  <bar><test1></test1></bar>
+  <bar><test2></test2></bar>
+  <fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test3></test3></fubar:bar>
+  <fubar:bar xmlns:fubar="http://www.example.com/ns/fubar"><test4></test4></fubar:bar>
+
+</contain>