mirror of
https://github.com/php/php-src.git
synced 2025-08-18 23:18:56 +02:00
add C14N() and C14NFile() methods to perform XML canonicalization
add test
This commit is contained in:
parent
2fd6c8607a
commit
d178e8d3e4
3 changed files with 291 additions and 0 deletions
|
@ -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);
|
||||
|
|
187
ext/dom/node.c
187
ext/dom/node.c
|
@ -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
|
||||
|
|
102
ext/dom/tests/canonicalization.phpt
Normal file
102
ext/dom/tests/canonicalization.phpt
Normal file
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue