/*
   +----------------------------------------------------------------------+
   | PHP Version 4                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2007 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: Uwe Steinmann <steinm@php.net>                               |
   +----------------------------------------------------------------------+
 */

/* $Id: php_domxml.c,v 1.218.2.50.2.5 2007/01/01 09:46:41 sebastian Exp $ */

/* TODO
 * - Support Notation Nodes
 * */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "ext/standard/php_rand.h"

#include "php_domxml.h"

#if HAVE_DOMXML
#include "ext/standard/info.h"
#define PHP_XPATH 1
#define PHP_XPTR 2

/* General macros used by domxml */

#define DOMXML_IS_TYPE(zval, ce)					(zval && Z_TYPE_P(zval) == IS_OBJECT && Z_OBJCE_P(zval)->refcount == ce->refcount)

#define DOMXML_DOMOBJ_NEW(zval, obj, ret)			if (NULL == (zval = php_domobject_new(obj, ret, zval TSRMLS_CC))) { \
														php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create required DOM object"); \
														RETURN_FALSE; \
													}

#define DOMXML_RET_ZVAL(zval)						SEPARATE_ZVAL(&zval); \
													*return_value = *zval; \
													FREE_ZVAL(zval);

#define DOMXML_RET_OBJ(zval, obj, ret)				DOMXML_DOMOBJ_NEW(zval, obj, ret); \
													DOMXML_RET_ZVAL(zval);

#define DOMXML_GET_THIS(zval)						if (NULL == (zval = getThis())) { \
														php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing"); \
														RETURN_FALSE; \
													}

#define DOMXML_GET_OBJ(ret, zval, le)				if (NULL == (ret = php_dom_get_object(zval, le, 0 TSRMLS_CC))) { \
														php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot fetch DOM object"); \
														RETURN_FALSE; \
													}

#define DOMXML_GET_THIS_OBJ(ret, zval, le)			DOMXML_GET_THIS(zval); \
													DOMXML_GET_OBJ(ret, zval, le);

#define DOMXML_NO_ARGS()							if (ZEND_NUM_ARGS() != 0) { \
														php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expects exactly 0 parameters, %d given", ZEND_NUM_ARGS()); \
														return; \
													}

#define DOMXML_NOT_IMPLEMENTED()					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not yet implemented"); \
													return;

/* WARNING: The number of parameters is actually the
 * number of passed variables to zend_parse_parameters(),
 * *NOT* the number of parameters expected by the PHP function. */
#define DOMXML_PARAM_NONE(ret, zval, le)			if (NULL == (zval = getThis())) { \
														if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zval) == FAILURE) { \
															return; \
														} \
													} \
													DOMXML_GET_OBJ(ret, zval, le);

#define DOMXML_PARAM_ONE(ret, zval, le, s, p1)	if (NULL == (zval = getThis())) { \
														if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o"s, &zval, p1) == FAILURE) { \
															return; \
														} \
													} else { \
														if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, s, p1) == FAILURE) { \
															return; \
														} \
													} \
													DOMXML_GET_OBJ(ret, zval, le);

#define DOMXML_PARAM_TWO(ret, zval, le, s, p1, p2)	if (NULL == (zval = getThis())) { \
														if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o"s, &zval, p1, p2) == FAILURE) { \
															return; \
														} \
													} else { \
														if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, s, p1, p2) == FAILURE) { \
															return; \
														} \
													} \
													DOMXML_GET_OBJ(ret, zval, le);

#define DOMXML_PARAM_THREE(ret, zval, le, s, p1, p2, p3)		if (NULL == (zval = getThis())) { \
																	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o"s, &zval, p1, p2, p3) == FAILURE) { \
																		return; \
																	} \
																} else { \
																	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, s, p1, p2, p3) == FAILURE) { \
																		return; \
																	} \
																} \
																DOMXML_GET_OBJ(ret, zval, le);


#define DOMXML_PARAM_FOUR(ret, zval, le, s, p1, p2, p3, p4)		if (NULL == (zval = getThis())) { \
																	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o"s, &zval, p1, p2, p3, p4) == FAILURE) { \
																		return; \
																	} \
																} else { \
																	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, s, p1, p2, p3, p4) == FAILURE) { \
																		return; \
																	} \
																} \
																DOMXML_GET_OBJ(ret, zval, le);

#define DOMXML_PARAM_SIX(ret, zval, le, s, p1, p2, p3, p4, p5, p6)		if (NULL == (zval = getThis())) { \
																	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o"s, &zval, p1, p2, p3, p4, p5, p6) == FAILURE) { \
																		return; \
																	} \
																} else { \
																	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, s, p1, p2, p3, p4, p5, p6) == FAILURE) { \
																		return; \
																	} \
																} \
																DOMXML_GET_OBJ(ret, zval, le);

#define DOMXML_LOAD_PARSING  0
#define DOMXML_LOAD_VALIDATING 1
#define DOMXML_LOAD_RECOVERING 2
#define DOMXML_LOAD_SUBSTITUTE_ENTITIES 4
#define DOMXML_LOAD_COMPLETE_ATTRS 8
#define DOMXML_LOAD_DONT_KEEP_BLANKS 16
#define DOMXML_LOAD_FILE 1

static int le_domxmldocp;
static int le_domxmldoctypep;
static int le_domxmldtdp;
static int le_domxmlnodep;
static int le_domxmlelementp;
static int le_domxmlattrp;
static int le_domxmlcdatap;
static int le_domxmltextp;
static int le_domxmlpip;
static int le_domxmlcommentp;
static int le_domxmlnotationp;
static int le_domxmlparserp;
static int le_domxmlnamespacep;

/*static int le_domxmlentityp;*/
static int le_domxmlentityrefp;
/*static int le_domxmlnsp;*/


#if HAVE_DOMXSLT
static int le_domxsltstylesheetp;
#endif
static void domxml_error(void *ctx, const char *msg, ...);
static void domxml_error_ext(void *ctx, const char *msg, ...);
static void domxml_error_validate(void *ctx, const char *msg, ...);

#if defined(LIBXML_XPATH_ENABLED)
static int le_xpathctxp;
static int le_xpathobjectp;
#endif

zend_class_entry *domxmldoc_class_entry;
zend_class_entry *domxmldoctype_class_entry;
zend_class_entry *domxmlelement_class_entry;
zend_class_entry *domxmldtd_class_entry;
zend_class_entry *domxmlnode_class_entry;
zend_class_entry *domxmlattr_class_entry;
zend_class_entry *domxmlcdata_class_entry;
zend_class_entry *domxmltext_class_entry;
zend_class_entry *domxmlpi_class_entry;
zend_class_entry *domxmlcomment_class_entry;
zend_class_entry *domxmlnotation_class_entry;
zend_class_entry *domxmlentity_class_entry;
zend_class_entry *domxmlentityref_class_entry;
zend_class_entry *domxmlns_class_entry;
zend_class_entry *domxmlparser_class_entry;
#if defined(LIBXML_XPATH_ENABLED)
zend_class_entry *xpathctx_class_entry;
zend_class_entry *xpathobject_class_entry;
#endif
#if HAVE_DOMXSLT
zend_class_entry *domxsltstylesheet_class_entry;
static int xslt_has_xsl_keys (xmlDocPtr doc);
#endif

static int node_attributes(zval **attributes, xmlNode *nodep TSRMLS_DC);
static int node_children(zval **children, xmlNode *nodep TSRMLS_DC);

static unsigned char first_args_force_ref[]  = { 1, BYREF_FORCE };
static unsigned char second_args_force_ref[] = { 2, BYREF_NONE, BYREF_FORCE };
static unsigned char third_args_force_ref[]  = { 3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };

static zend_function_entry domxml_functions[] = {
	PHP_FE(domxml_version,												NULL)
	PHP_FE(xmldoc,														third_args_force_ref)
	PHP_FALIAS(domxml_open_mem,				xmldoc,	third_args_force_ref)
	PHP_FE(xmldocfile,														third_args_force_ref)
	PHP_FALIAS(domxml_open_file,				xmldocfile,	third_args_force_ref)
#if defined(LIBXML_HTML_ENABLED)
	PHP_FE(html_doc,													NULL)
	PHP_FE(html_doc_file,												NULL)
#endif
	PHP_FE(domxml_xmltree,														NULL)
	PHP_FALIAS(xmltree,				domxml_xmltree,	NULL)
	PHP_FE(domxml_substitute_entities_default,														NULL)
	PHP_FE(domxml_doc_document_element,									NULL)
	PHP_FE(domxml_doc_add_root,											NULL)
	PHP_FE(domxml_doc_set_root,											NULL)
	PHP_FE(domxml_dump_mem,												NULL)
	PHP_FE(domxml_dump_mem_file,										NULL)
	PHP_FE(domxml_dump_node,											NULL)
#if defined(LIBXML_HTML_ENABLED)
	PHP_FE(domxml_html_dump_mem,										NULL)
#endif
	PHP_FE(domxml_node_attributes,										NULL)
	PHP_FE(domxml_elem_get_attribute,									NULL)
	PHP_FE(domxml_elem_set_attribute,									NULL)
	PHP_FE(domxml_node_children,										NULL)
	PHP_FE(domxml_node_has_attributes,									NULL)
	PHP_FE(domxml_node_new_child,										NULL)
	PHP_FE(domxml_node,													NULL)
	PHP_FE(domxml_node_unlink_node,										NULL)
	PHP_FE(domxml_node_set_content,										NULL)
	PHP_FE(domxml_node_get_content,										NULL)
	PHP_FE(domxml_node_add_namespace,									NULL)
	PHP_FE(domxml_node_set_namespace,									NULL)
	PHP_FE(domxml_new_xmldoc,											NULL)
	PHP_FALIAS(domxml_new_doc,				domxml_new_xmldoc,	NULL)
	PHP_FE(domxml_parser,												NULL)
	PHP_FE(domxml_parser_add_chunk,										NULL)
	PHP_FE(domxml_parser_end,											NULL)
	PHP_FE(domxml_parser_start_element,									NULL)
	PHP_FE(domxml_parser_end_element,									NULL)
	PHP_FE(domxml_parser_comment,										NULL)
	PHP_FE(domxml_parser_characters,									NULL)
	PHP_FE(domxml_parser_entity_reference,								NULL)
	PHP_FE(domxml_parser_processing_instruction,						NULL)
	PHP_FE(domxml_parser_cdata_section,									NULL)
	PHP_FE(domxml_parser_namespace_decl,								NULL)
	PHP_FE(domxml_parser_start_document,								NULL)
	PHP_FE(domxml_parser_end_document,									NULL)
	PHP_FE(domxml_parser_get_document,									NULL)

#if defined(LIBXML_XPATH_ENABLED)
	PHP_FE(xpath_new_context,											NULL)
	PHP_FE(xpath_eval,													NULL)
	PHP_FE(xpath_eval_expression,										NULL)
	PHP_FE(xpath_register_ns,											NULL)
	PHP_FE(domxml_doc_get_elements_by_tagname,							NULL)
	PHP_FE(domxml_doc_get_element_by_id,								NULL)
#endif
#if defined(LIBXML_XPTR_ENABLED)
	PHP_FE(xptr_new_context,											NULL)
	PHP_FE(xptr_eval,													NULL)
#endif
#if HAVE_DOMXSLT
	PHP_FE(domxml_xslt_version,											NULL)
	PHP_FE(domxml_xslt_stylesheet,										NULL)
	PHP_FE(domxml_xslt_stylesheet_doc,									NULL)
	PHP_FE(domxml_xslt_stylesheet_file,									NULL)
	PHP_FE(domxml_xslt_process,											NULL)
	PHP_FE(domxml_xslt_result_dump_mem,							  			NULL)
	PHP_FE(domxml_xslt_result_dump_file,							  			NULL)
#endif

	PHP_FALIAS(domxml_add_root,			domxml_doc_add_root,			NULL)
	PHP_FALIAS(domxml_doc_get_root,		domxml_doc_document_element,	NULL)
	PHP_FALIAS(domxml_root,				domxml_doc_document_element,	NULL)
	PHP_FALIAS(domxml_attributes,		domxml_node_attributes,			NULL)
	PHP_FALIAS(domxml_get_attribute,	domxml_elem_get_attribute,		NULL)
	PHP_FALIAS(domxml_getattr,			domxml_elem_get_attribute,		NULL)
	PHP_FALIAS(domxml_set_attribute,	domxml_elem_set_attribute,		NULL)
	PHP_FALIAS(domxml_setattr,			domxml_elem_set_attribute,		NULL)
	PHP_FALIAS(domxml_children,			domxml_node_children,			NULL)
	PHP_FALIAS(domxml_new_child,		domxml_node_new_child,			NULL)
	PHP_FALIAS(domxml_unlink_node,		domxml_node_unlink_node,		NULL)
	PHP_FALIAS(set_content,				domxml_node_set_content,		NULL)
	PHP_FALIAS(new_xmldoc,				domxml_new_xmldoc,				NULL)
	PHP_FALIAS(domxml_dumpmem,			domxml_dump_mem,				NULL)
	PHP_FE(domxml_doc_validate,											second_args_force_ref)
	PHP_FE(domxml_doc_xinclude,											NULL)
	{NULL, NULL, NULL}
};


static function_entry php_domxmldoc_class_functions[] = {
	PHP_FALIAS(domdocument,				xmldoc,							NULL)
	PHP_FALIAS(doctype, 				domxml_doc_doctype, 			NULL)
	PHP_FALIAS(implementation,			domxml_doc_implementation,		NULL)
	PHP_FALIAS(document_element,		domxml_doc_document_element,	NULL)
	PHP_FALIAS(create_element,			domxml_doc_create_element,		NULL)
	PHP_FALIAS(create_element_ns,		domxml_doc_create_element_ns,	NULL)
	PHP_FALIAS(create_text_node,		domxml_doc_create_text_node,	NULL)
	PHP_FALIAS(create_comment,			domxml_doc_create_comment,		NULL)
	PHP_FALIAS(create_attribute,		domxml_doc_create_attribute,	NULL)
	PHP_FALIAS(create_cdata_section,	domxml_doc_create_cdata_section,	NULL)
	PHP_FALIAS(create_entity_reference,	domxml_doc_create_entity_reference,	NULL)
	PHP_FALIAS(create_processing_instruction,	domxml_doc_create_processing_instruction,	NULL)
	PHP_FALIAS(get_elements_by_tagname,	domxml_doc_get_elements_by_tagname,	NULL)
	PHP_FALIAS(get_element_by_id,		domxml_doc_get_element_by_id,	NULL)
	PHP_FALIAS(free,					domxml_doc_free_doc,			NULL)
	/* Everything below this comment is none DOM compliant */
	/* children is deprecated because it is inherited from DomNode */
/*	PHP_FALIAS(children,				domxml_node_children,			NULL) */
	PHP_FALIAS(add_root,				domxml_doc_add_root,			NULL)
	PHP_FALIAS(set_root,				domxml_doc_set_root,			NULL)
	PHP_FALIAS(get_root,				domxml_doc_document_element,	NULL)
	PHP_FALIAS(root,					domxml_doc_document_element,	NULL)
	PHP_FALIAS(imported_node,			domxml_doc_imported_node,		NULL)
	PHP_FALIAS(dtd,						domxml_intdtd,					NULL)
	PHP_FALIAS(ids,						domxml_doc_ids,					NULL)
	PHP_FALIAS(dumpmem,					domxml_dump_mem,				NULL)
	PHP_FALIAS(dump_mem,				domxml_dump_mem,				NULL)
	PHP_FALIAS(dump_mem_file,			domxml_dump_mem_file,			NULL)
	PHP_FALIAS(dump_file,				domxml_dump_mem_file,			NULL)
#if defined(LIBXML_HTML_ENABLED)
	PHP_FALIAS(html_dump_mem,			domxml_html_dump_mem,			NULL)
#endif
#if defined(LIBXML_XPATH_ENABLED)
	PHP_FALIAS(xpath_init,				xpath_init,						NULL)
	PHP_FALIAS(xpath_new_context,		xpath_new_context,				NULL)
	PHP_FALIAS(xptr_new_context,		xptr_new_context,				NULL)
#endif
	PHP_FALIAS(validate,				domxml_doc_validate,				first_args_force_ref)
	PHP_FALIAS(xinclude,				domxml_doc_xinclude,				NULL)

	{NULL, NULL, NULL}
};

static function_entry php_domxmlparser_class_functions[] = {
	PHP_FALIAS(add_chunk,				domxml_parser_add_chunk,		NULL)
	PHP_FALIAS(end,						domxml_parser_end,				NULL)
	PHP_FALIAS(set_keep_blanks,			domxml_parser_set_keep_blanks,	NULL)
	PHP_FALIAS(start_element,				domxml_parser_start_element,		NULL)
	PHP_FALIAS(end_element,				domxml_parser_end_element,		NULL)
	PHP_FALIAS(characters,				domxml_parser_characters,		NULL)
	PHP_FALIAS(entity_reference,				domxml_parser_entity_reference,		NULL)
	PHP_FALIAS(processing_instruction,				domxml_parser_processing_instruction,		NULL)
	PHP_FALIAS(cdata_section,				domxml_parser_cdata_section,		NULL)
	PHP_FALIAS(comment,				domxml_parser_comment,		NULL)
	PHP_FALIAS(namespace_decl,				domxml_parser_namespace_decl,		NULL)
	PHP_FALIAS(start_document,				domxml_parser_start_document,		NULL)
	PHP_FALIAS(end_document,				domxml_parser_end_document,		NULL)
	PHP_FALIAS(get_document,				domxml_parser_get_document,		NULL)

	{NULL, NULL, NULL}
};

static function_entry php_domxmldoctype_class_functions[] = {
	PHP_FALIAS(name,					domxml_doctype_name,			NULL)
	PHP_FALIAS(entities,				domxml_doctype_entities,		NULL)
	PHP_FALIAS(notations,				domxml_doctype_notations,		NULL)
	PHP_FALIAS(system_id,				domxml_doctype_system_id,		NULL)
	PHP_FALIAS(public_id,				domxml_doctype_public_id,		NULL)
/*
	PHP_FALIAS(internal_subset,			domxml_doctype_internal_subset,	NULL)
*/
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmldtd_class_functions[] = {
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmlnode_class_functions[] = {
	PHP_FALIAS(domnode,					domxml_node,					NULL)
	PHP_FALIAS(node_name,				domxml_node_name,				NULL)
	PHP_FALIAS(node_type,				domxml_node_type,				NULL)
	PHP_FALIAS(node_value,				domxml_node_value,				NULL)
	PHP_FALIAS(first_child,				domxml_node_first_child,		NULL)
	PHP_FALIAS(last_child,				domxml_node_last_child,			NULL)
	PHP_FALIAS(children,				domxml_node_children,			NULL)
	PHP_FALIAS(child_nodes,				domxml_node_children,			NULL)
	PHP_FALIAS(previous_sibling,		domxml_node_previous_sibling,	NULL)
	PHP_FALIAS(next_sibling,			domxml_node_next_sibling,		NULL)
	PHP_FALIAS(has_child_nodes,			domxml_node_has_child_nodes,	NULL)
	PHP_FALIAS(parent,					domxml_node_parent,				NULL)
	PHP_FALIAS(parent_node,				domxml_node_parent,				NULL)
	PHP_FALIAS(insert_before,			domxml_node_insert_before,		NULL)
	PHP_FALIAS(append_child,			domxml_node_append_child,		NULL)
	PHP_FALIAS(remove_child,			domxml_node_remove_child,		NULL)
	PHP_FALIAS(replace_child,			domxml_node_replace_child,		NULL)
	PHP_FALIAS(owner_document,			domxml_node_owner_document,		NULL)
	PHP_FALIAS(new_child,				domxml_node_new_child,			NULL)
	PHP_FALIAS(attributes,				domxml_node_attributes,			NULL)
	PHP_FALIAS(has_attributes,			domxml_node_has_attributes,		NULL)
	PHP_FALIAS(prefix,				domxml_node_prefix,				NULL)
	PHP_FALIAS(namespace_uri,			domxml_node_namespace_uri,				NULL)
	PHP_FALIAS(clone_node,				domxml_clone_node,				NULL)
/* Non DOM functions start here */
	PHP_FALIAS(add_namespace,			domxml_node_add_namespace,		NULL)
	PHP_FALIAS(set_namespace,			domxml_node_set_namespace,		NULL)
	PHP_FALIAS(add_child,				domxml_node_append_child,		NULL)
	PHP_FALIAS(append_sibling,			domxml_node_append_sibling,		NULL)
	PHP_FALIAS(node,					domxml_node,					NULL)
	PHP_FALIAS(unlink,					domxml_node_unlink_node,		NULL)
	PHP_FALIAS(unlink_node,				domxml_node_unlink_node,		NULL)
	PHP_FALIAS(replace_node,			domxml_node_replace_node,		NULL)
	PHP_FALIAS(set_content,				domxml_node_set_content,		NULL)
	PHP_FALIAS(get_content,				domxml_node_get_content,		NULL)
	PHP_FALIAS(text_concat,				domxml_node_text_concat,		NULL)
	PHP_FALIAS(set_name,				domxml_node_set_name,			NULL)
	PHP_FALIAS(is_blank_node,			domxml_is_blank_node,			NULL)
	PHP_FALIAS(dump_node,				domxml_dump_node,				NULL)
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmlelement_class_functions[] = {
	PHP_FALIAS(domelement,				domxml_doc_create_element,		NULL)
	PHP_FALIAS(name,					domxml_elem_tagname,			NULL)
	PHP_FALIAS(tagname,					domxml_elem_tagname,			NULL)
	PHP_FALIAS(get_attribute,			domxml_elem_get_attribute,		NULL)
	PHP_FALIAS(set_attribute,			domxml_elem_set_attribute,		NULL)
	PHP_FALIAS(remove_attribute,		domxml_elem_remove_attribute,	NULL)
	PHP_FALIAS(get_attribute_node,		domxml_elem_get_attribute_node,	NULL)
/* since this function is not implemented, outcomment it for the time beeing
	PHP_FALIAS(set_attribute_node,		domxml_elem_set_attribute_node,	NULL)
*/
	PHP_FALIAS(get_elements_by_tagname,	domxml_elem_get_elements_by_tagname,	NULL)
	PHP_FALIAS(has_attribute,	domxml_elem_has_attribute,	NULL)
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmlcdata_class_functions[] = {
	PHP_FALIAS(domcdata,				domxml_doc_create_cdata_section,NULL)
	PHP_FALIAS(length,					domxml_cdata_length,			NULL)
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmltext_class_functions[] = {
	PHP_FALIAS(domtext,					domxml_doc_create_text_node,	NULL)
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmlcomment_class_functions[] = {
	PHP_FALIAS(domcomment,				domxml_doc_create_comment,		NULL)
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmlnotation_class_functions[] = {
	PHP_FALIAS(public_id,				domxml_notation_public_id,		NULL)
	PHP_FALIAS(system_id,				domxml_notation_system_id,		NULL)
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmlentityref_class_functions[] = {
	PHP_FALIAS(domentityreference,		domxml_doc_create_entity_reference,	NULL)
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmlentity_class_functions[] = {
/*
	PHP_FALIAS(public_id,				domxml_entity_public_id,		NULL)
	PHP_FALIAS(system_id,				domxml_entity_system_id,		NULL)
	PHP_FALIAS(notation_name,			domxml_entity_notation_name,	NULL)
*/
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmlpi_class_functions[] = {
	PHP_FALIAS(domprocessinginstruction, domxml_doc_create_processing_instruction, NULL)
	PHP_FALIAS(target,					domxml_pi_target,				NULL)
	PHP_FALIAS(data,					domxml_pi_data,					NULL)
	{NULL, NULL, NULL}
};

#if defined(LIBXML_XPATH_ENABLED)
static zend_function_entry php_xpathctx_class_functions[] = {
	PHP_FALIAS(xpath_eval,				xpath_eval,						NULL)
	PHP_FALIAS(xpath_eval_expression,	xpath_eval_expression,			NULL)
	PHP_FALIAS(xpath_register_ns,		xpath_register_ns,				NULL)
	{NULL, NULL, NULL}
};

static zend_function_entry php_xpathobject_class_functions[] = {
	{NULL, NULL, NULL}
};
#endif

static zend_function_entry php_domxmlattr_class_functions[] = {
	PHP_FALIAS(domattribute,			domxml_doc_create_attribute,	NULL)
	/* DOM_XML Consistent calls */
	PHP_FALIAS(node_name,				domxml_attr_name,				NULL)
	PHP_FALIAS(node_value,				domxml_attr_value,				NULL)
	PHP_FALIAS(node_specified,			domxml_attr_specified,			NULL)
	/* W3C compliant calls */
	PHP_FALIAS(name,					domxml_attr_name,				NULL)
	PHP_FALIAS(value,					domxml_attr_value,				NULL)
	PHP_FALIAS(specified,				domxml_attr_specified,			NULL)
/*
	PHP_FALIAS(owner_element,			domxml_attr_owner_element,		NULL)
*/
	{NULL, NULL, NULL}
};

static zend_function_entry php_domxmlns_class_functions[] = {
	PHP_FALIAS(node_name,				domxml_node_name,				NULL)
	PHP_FALIAS(node_type,				domxml_node_type,				NULL)
	PHP_FALIAS(node_value,				domxml_node_value,			NULL)
	PHP_FALIAS(prefix,				domxml_node_prefix,			NULL)
	PHP_FALIAS(namespace_uri,			domxml_node_namespace_uri,		NULL)
	PHP_FALIAS(owner_document,			domxml_node_owner_document,		NULL)
	PHP_FALIAS(parent_node,				domxml_node_parent,			NULL)
	{NULL, NULL, NULL}
};

#if HAVE_DOMXSLT
static zend_function_entry php_domxsltstylesheet_class_functions[] = {
/* TODO: maybe some more methods? */
	PHP_FALIAS(process, 				domxml_xslt_process, 			NULL)
	PHP_FALIAS(result_dump_mem, 			domxml_xslt_result_dump_mem, 		NULL)
	PHP_FALIAS(result_dump_file, 			domxml_xslt_result_dump_file, 		NULL)
	{NULL, NULL, NULL}
};
#endif

zend_module_entry domxml_module_entry = {
	STANDARD_MODULE_HEADER,
	"domxml",
	domxml_functions,
	PHP_MINIT(domxml),
	PHP_MSHUTDOWN(domxml),
	PHP_RINIT(domxml),
	PHP_RSHUTDOWN(domxml),
	PHP_MINFO(domxml),
	DOMXML_API_VERSION, /* Extension versionnumber */
	STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_DOMXML
ZEND_GET_MODULE(domxml)
#endif


static void dom_object_set_data(void *obj, zval *wrapper)
{
/*
	char tmp[20];
	sprintf(tmp, "%08X", obj);
	fprintf(stderr, "Adding %s to hash\n", tmp);
*/
	((xmlNodePtr) obj)->_private = wrapper;
}


static zval *dom_object_get_data(void *obj)
{
/*	char tmp[20];
	sprintf(tmp, "%08X", obj);
	fprintf(stderr, "Trying getting %s from object ...", tmp);
	if(((xmlNodePtr) obj)->_private)
		fprintf(stderr, " found\n");
	else
		fprintf(stderr, " not found\n");
*/
	return ((zval *) (((xmlNodePtr) obj)->_private));
}


static inline void node_wrapper_dtor(xmlNodePtr node)
{
	zval *wrapper;
	int refcount = 0;
	/* FIXME: type check probably unnecessary here? */
	if (!node) /* || Z_TYPE_P(node) == XML_DTD_NODE)*/
		return;

	wrapper = dom_object_get_data(node);

	if (wrapper != NULL ) {
		refcount = wrapper->refcount;
		zval_ptr_dtor(&wrapper);
		/*only set it to null, if refcount was 1 before, otherwise it has still needed references */
		if (refcount == 1) {
			dom_object_set_data(node, NULL);
		}
	}

}


/*	This function should only be used when freeing nodes 
	as dependant objects are destroyed */
static inline void node_wrapper_free(xmlNodePtr node  TSRMLS_DC)
{
	zval *wrapper, **handle;
	int type, refcount = 0;

	if (!node) {
		return;
	}

	wrapper = dom_object_get_data(node);
	if (wrapper != NULL ) {
		/* All references need to be destroyed */
		if (zend_hash_index_find(Z_OBJPROP_P(wrapper), 0, (void **) &handle) ==	SUCCESS) {
			if (zend_list_find(Z_LVAL_PP(handle), &type)) {
				zend_list_delete(Z_LVAL_PP(handle));
			}
		} else {
			refcount = wrapper->refcount;
			zval_ptr_dtor(&wrapper);

			/* only set it to null, if refcount was 1 before, otherwise it has still needed references */
			if (refcount == 1) {
				dom_object_set_data(node, NULL);
			}
		}
	}

}

static inline void attr_list_wrapper_dtor(xmlAttrPtr attr)
{
	while (attr != NULL) {
		/* Attribute nodes contain children which can be accessed */
		node_wrapper_dtor((xmlNodePtr) attr);
		attr = attr->next;
	}
}

/*	destroyref is a bool indicating if all registered objects for nodes 
	within the tree should be destroyed */
static inline void node_list_wrapper_dtor(xmlNodePtr node, int destroyref TSRMLS_DC)
{
	while (node != NULL) {
		node_list_wrapper_dtor(node->children, destroyref TSRMLS_CC);
		switch (node->type) {
			/* Skip property freeing for the following types */
			case XML_ATTRIBUTE_DECL:
			case XML_DTD_NODE:
			case XML_ELEMENT_DECL:
			case XML_ENTITY_DECL:
			case XML_ATTRIBUTE_NODE:
				break;
			default:
				/*	Attribute Nodes contain accessible children
					Call this function with the propert list
				attr_list_wrapper_dtor(node->properties);  */
				node_list_wrapper_dtor((xmlNodePtr) node->properties, destroyref TSRMLS_CC);
		}

		if (destroyref == 1) {
			node_wrapper_free(node TSRMLS_CC);
		} else {
			node_wrapper_dtor(node);
		}

		node = node->next;
	}
}

/*	Navigate through the tree and unlink nodes which are referenced by objects */
static inline void node_list_unlink(xmlNodePtr node)
{
	zval *wrapper;

	while (node != NULL) {

		wrapper = dom_object_get_data(node);

		if (wrapper != NULL ) {
			/* This node is referenced so no need to check children */
			xmlUnlinkNode(node);
		} else {
			node_list_unlink(node->children);

			switch (node->type) {
				/* Skip property freeing for the following types */
				case XML_ATTRIBUTE_DECL:
				case XML_DTD_NODE:
				case XML_ENTITY_DECL:
				case XML_ELEMENT_DECL:
				case XML_ATTRIBUTE_NODE:
					break;
				default:
					node_list_unlink((xmlNodePtr) node->properties);
			}
		}

		node = node->next;
	}
}

static xmlNodeSetPtr php_get_elements_by_tagname(xmlNodePtr n, xmlChar* name, xmlNodeSet *rv )
{
	xmlNodePtr cld = NULL;
	/* TODO
	   Namespace support
	 */
	if ( n != NULL && name != NULL ) {
		cld = n;
		while ( cld != NULL ) {
			if (cld->type == XML_ELEMENT_NODE && (xmlStrEqual(name, "*") || xmlStrcmp(name, cld->name) == 0)){
				if ( rv == NULL ) {
					rv = xmlXPathNodeSetCreate( cld ) ;
				}
				else {
					xmlXPathNodeSetAdd( rv, cld );
				}
			}
			rv = php_get_elements_by_tagname(cld->children, name, rv);
			cld = cld->next;
		}
	}
	return rv;
}

static void php_free_xml_doc(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
	xmlDoc *doc = (xmlDoc *) rsrc->ptr;

	if (doc) {
		node_list_wrapper_dtor(doc->children, 1 TSRMLS_CC);
		node_wrapper_dtor((xmlNodePtr) doc);
		xmlFreeDoc(doc);
	}
}

static void php_free_xml_node(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
	xmlNodePtr node = (xmlNodePtr) rsrc->ptr;

	/* if node has no parent, it will not be freed by php_free_xml_doc, so do it here
	and for all children as well. */
	if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
		/* Attribute Nodes ccontain accessible children 
		attr_list_wrapper_dtor(node->properties); */
		xmlSetTreeDoc(node, NULL);
		if (node->type == XML_NAMESPACE_DECL) {
			if (node->ns) {
				xmlFreeNs(node->ns);
				node->ns = NULL;
			}
			node->type = XML_ELEMENT_NODE;
		} else {
			node_list_wrapper_dtor((xmlNodePtr) node->properties, 1 TSRMLS_CC);
		}
		node_list_wrapper_dtor(node->children, 1 TSRMLS_CC);
		node_wrapper_dtor(node);
		xmlFreeNode(node);
	} else {
		node_wrapper_dtor(node);
	}
}

static void php_free_xml_attr(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
	xmlNodePtr node = (xmlNodePtr) rsrc->ptr;
	if (node->parent == NULL) {
		/* Attribute Nodes contain accessible children */
		node_list_wrapper_dtor(node->children, 1 TSRMLS_CC);
		node_wrapper_dtor(node);
		xmlFreeProp((xmlAttrPtr) node);
	} else {
		node_wrapper_dtor(node);
	}
}


#if defined(LIBXML_XPATH_ENABLED)
static void php_free_xpath_context(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
	xmlXPathContextPtr ctx = (xmlXPathContextPtr) rsrc->ptr;
	if (ctx) {
		if (ctx->user) {
			zval *wrapper = ctx->user;
			zval_ptr_dtor(&wrapper);
		}
		xmlXPathFreeContext(ctx);
	}
}
#endif

static void php_free_xml_parser(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
	xmlParserCtxtPtr parser = (xmlParserCtxtPtr) rsrc->ptr;

	if (parser) {
		zval *wrapper = dom_object_get_data(parser);
		zval_ptr_dtor(&wrapper);
		xmlFreeParserCtxt(parser);
	}
}


#if HAVE_DOMXSLT
static void xsltstylesheet_set_data(void *obj, zval *wrapper)
{
	((xsltStylesheetPtr) obj)->_private = wrapper;
}

static zval *xsltstylesheet_get_data(void *obj)
{
	return ((zval *) (((xsltStylesheetPtr) obj)->_private));
}

static void php_free_xslt_stylesheet(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
	xsltStylesheetPtr sheet = (xsltStylesheetPtr) rsrc->ptr;
	zval *wrapper;
	int refcount = 0;

	if (sheet) {
		wrapper = xsltstylesheet_get_data(sheet);

		if (wrapper != NULL ) {
			refcount = wrapper->refcount;
			zval_ptr_dtor(&wrapper);

			/* only set it to null, if refcount was 1 before, otherwise it has still needed references */
			if (refcount == 1) {
				xsltstylesheet_set_data(sheet, NULL);
			}
		}
		xsltFreeStylesheet(sheet);
	}
}

static void *php_xsltstylesheet_get_object(zval *wrapper, int rsrc_type1, int rsrc_type2 TSRMLS_DC)
{
	void *obj;
	zval **handle;
	int type;

	if (NULL == wrapper) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "xsltstylesheet_get_object() invalid wrapper object passed");
		return NULL;
	}

	if (Z_TYPE_P(wrapper) != IS_OBJECT) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "wrapper is not an object");
		return NULL;
	}

	if (zend_hash_index_find(Z_OBJPROP_P(wrapper), 0, (void **) &handle) ==	FAILURE) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing");
		return NULL;
	}

	obj = zend_list_find(Z_LVAL_PP(handle), &type);
	if (!obj || ((type != rsrc_type1) && (type != rsrc_type2))) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing or of invalid type");
		return NULL;
	}

	return obj;
}

static void php_xsltstylesheet_set_object(zval *wrapper, void *obj, int rsrc_type TSRMLS_DC)
{
	zval *handle, *addr;

	MAKE_STD_ZVAL(handle);
	Z_TYPE_P(handle) = IS_LONG;
	Z_LVAL_P(handle) = zend_list_insert(obj, rsrc_type);

	MAKE_STD_ZVAL(addr);
	Z_TYPE_P(addr) = IS_LONG;
	Z_LVAL_P(addr) = (long) obj;

	zend_hash_index_update(Z_OBJPROP_P(wrapper), 0, &handle, sizeof(zval *), NULL);
	zend_hash_index_update(Z_OBJPROP_P(wrapper), 1, &addr, sizeof(zval *), NULL);
	zval_add_ref(&wrapper);
	xsltstylesheet_set_data(obj, wrapper);
}
#endif  /* HAVE_DOMXSLT */

/* Not used? */
/*
static void *php_xpath_get_object(zval *wrapper, int rsrc_type1, int rsrc_type2 TSRMLS_DC)
{
	void *obj;
	zval **handle;
	int type;

	if (NULL == wrapper) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_xpath_get_object() invalid wrapper object passed");
		return NULL;
	}

	if (Z_TYPE_P(wrapper) != IS_OBJECT) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "wrapper is not an object");
		return NULL;
	}

	if (zend_hash_index_find(Z_OBJPROP_P(wrapper), 0, (void **) &handle) ==	FAILURE) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing");
		return NULL;
	}

	obj = zend_list_find(Z_LVAL_PP(handle), &type);
	if (!obj || ((type != rsrc_type1) && (type != rsrc_type2))) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing or of invalid type");
		return NULL;
	}

	return obj;
}
*/

static zval *php_xpathobject_new(xmlXPathObjectPtr obj, int *found TSRMLS_DC)
{
	zval *wrapper;

	MAKE_STD_ZVAL(wrapper);
	object_init_ex(wrapper, xpathobject_class_entry);
	return (wrapper);
}

static void *php_xpath_get_context(zval *wrapper, int rsrc_type1, int rsrc_type2 TSRMLS_DC)
{
	void *obj;
	zval **handle;
	int type;

	if (NULL == wrapper) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_xpath_get_context() invalid wrapper object passed");
		return NULL;
	}

	if (Z_TYPE_P(wrapper) != IS_OBJECT) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "wrapper is not an object");
		return NULL;
	}

	if (zend_hash_index_find(Z_OBJPROP_P(wrapper), 0, (void **) &handle) ==
		FAILURE) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing");
		return NULL;
	}

	obj = zend_list_find(Z_LVAL_PP(handle), &type);
	if (!obj || ((type != rsrc_type1) && (type != rsrc_type2))) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing or of invalid type");
		return NULL;
	}

	return obj;
}

static void xpath_context_set_data(void *obj, zval *wrapper)
{
/*
	char tmp[20];
	sprintf(tmp, "%08X", obj);
	fprintf(stderr, "Adding %s to hash\n", tmp);
*/
	((xmlXPathContextPtr) obj)->user = (void *) wrapper;
}

static zval *xpath_context_get_data(void *obj)
{
/*
	char tmp[20];
	sprintf(tmp, "%08X", obj);
	fprintf(stderr, "Trying getting %s from hash ...", tmp);
	if(((xmlXPathContextPtr) obj)->user)
		fprintf(stderr, " found\n");
	else
		fprintf(stderr, " not found\n");
*/
	return ((zval *) (((xmlXPathContextPtr) obj)->user));
}

static void php_xpath_set_context(zval *wrapper, void *obj, int rsrc_type TSRMLS_DC)
{
	zval *handle, *addr;

	MAKE_STD_ZVAL(handle);
	Z_TYPE_P(handle) = IS_LONG;
	Z_LVAL_P(handle) = zend_list_insert(obj, rsrc_type);

	MAKE_STD_ZVAL(addr);
	Z_TYPE_P(addr) = IS_LONG;
	Z_LVAL_P(addr) = (long) obj;

	zend_hash_index_update(Z_OBJPROP_P(wrapper), 0, &handle, sizeof(zval *), NULL);
	zend_hash_index_update(Z_OBJPROP_P(wrapper), 1, &addr, sizeof(zval *), NULL);
	zval_add_ref(&wrapper);
	xpath_context_set_data(obj, wrapper);
}

static zval *php_xpathcontext_new(xmlXPathContextPtr obj, int *found TSRMLS_DC)
{
	zval *wrapper;
	int rsrc_type;

		*found = 0;

	if (!obj) {
		MAKE_STD_ZVAL(wrapper);
		ZVAL_NULL(wrapper);
		return wrapper;
	}

	if ((wrapper = (zval *) xpath_context_get_data((void *) obj))) {
		zval_add_ref(&wrapper);
		*found = 1;
		return wrapper;
	}

	MAKE_STD_ZVAL(wrapper);
/*
	fprintf(stderr, "Adding new XPath Context\n");
*/
	object_init_ex(wrapper, xpathctx_class_entry);
	rsrc_type = le_xpathctxp;
	php_xpath_set_context(wrapper, (void *) obj, rsrc_type TSRMLS_CC);

	return (wrapper);
}

/* helper functions for xmlparser stuff */
static void xmlparser_set_data(void *obj, zval *wrapper)
{
	((xmlParserCtxtPtr) obj)->_private = wrapper;
}


static void php_xmlparser_set_object(zval *wrapper, void *obj, int rsrc_type TSRMLS_DC)
{
	zval *handle, *addr;

	MAKE_STD_ZVAL(handle);
	Z_TYPE_P(handle) = IS_LONG;
	Z_LVAL_P(handle) = zend_list_insert(obj, rsrc_type);

	MAKE_STD_ZVAL(addr);
	Z_TYPE_P(addr) = IS_LONG;
	Z_LVAL_P(addr) = (long) obj;

	zend_hash_index_update(Z_OBJPROP_P(wrapper), 0, &handle, sizeof(zval *), NULL);
	zend_hash_index_update(Z_OBJPROP_P(wrapper), 1, &addr, sizeof(zval *), NULL);
	zval_add_ref(&wrapper);
	xmlparser_set_data(obj, wrapper);
}


static zval *php_xmlparser_new(xmlParserCtxtPtr obj, int *found TSRMLS_DC)
{
	zval *wrapper;
	int rsrc_type;

		*found = 0;

	if (!obj) {
		MAKE_STD_ZVAL(wrapper);
		ZVAL_NULL(wrapper);
		return wrapper;
	}

	MAKE_STD_ZVAL(wrapper);
	object_init_ex(wrapper, domxmlparser_class_entry);
	rsrc_type = le_domxmlparserp;
	php_xmlparser_set_object(wrapper, (void *) obj, rsrc_type TSRMLS_CC);

	return (wrapper);
}

/* {{{ php_xmlparser_make_params()
   Translates a PHP array to a xmlparser parameters array */
static char **php_xmlparser_make_params(zval *idvars TSRMLS_DC)
{
	HashTable *parht;
	int parsize;
	zval **value;
	char *expr, *string_key = NULL;
	ulong num_key;
	char **params = NULL;
	int i = 0;

	parht = HASH_OF(idvars);
	parsize = (2 * zend_hash_num_elements(parht) + 1) * sizeof(char *);
	params = (char **)emalloc(parsize);
	memset((char *)params, 0, parsize);

	for (zend_hash_internal_pointer_reset(parht);
		zend_hash_get_current_data(parht, (void **)&value) == SUCCESS;
		zend_hash_move_forward(parht)) {

		if (zend_hash_get_current_key(parht, &string_key, &num_key, 1) != HASH_KEY_IS_STRING) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid argument or parameter array");
			return NULL;
		}
		else {
			SEPARATE_ZVAL(value);
			convert_to_string_ex(value);
			expr = Z_STRVAL_PP(value);
	
			if (expr) {
				params[i++] = string_key;
				params[i++] = expr;
			}
		}
	}

	params[i++] = NULL;

	return params;
}
/* }}} */
/* end parser stuff */

static void *php_dom_get_object(zval *wrapper, int rsrc_type1, int rsrc_type2 TSRMLS_DC)
{
	void *obj;
	zval **handle;
	int type;

	if (NULL == wrapper) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_dom_get_object() invalid wrapper object passed");
		return NULL;
	}

	if (Z_TYPE_P(wrapper) != IS_OBJECT) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "wrapper is not an object");
		return NULL;
	}

	if (zend_hash_index_find(Z_OBJPROP_P(wrapper), 0, (void **) &handle) ==	FAILURE) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing");
		return NULL;
	}

	obj = zend_list_find(Z_LVAL_PP(handle), &type);

/* The following test should be replaced with search in all parents */
	if (!obj) {		/* || ((type != rsrc_type1) && (type != rsrc_type2))) { */
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing or of invalid type");
		return NULL;
	}

	return obj;
}


static void php_dom_set_object(zval *wrapper, void *obj, int rsrc_type TSRMLS_DC)
{
	zval *handle, *addr;

	MAKE_STD_ZVAL(handle);
	Z_TYPE_P(handle) = IS_LONG;
	Z_LVAL_P(handle) = zend_list_insert(obj, rsrc_type);

	MAKE_STD_ZVAL(addr);
	Z_TYPE_P(addr) = IS_LONG;
	Z_LVAL_P(addr) = (long) obj;

	zend_hash_index_update(Z_OBJPROP_P(wrapper), 0, &handle, sizeof(zval *), NULL);
	zend_hash_index_update(Z_OBJPROP_P(wrapper), 1, &addr, sizeof(zval *), NULL);
	zval_add_ref(&wrapper);
	dom_object_set_data(obj, wrapper);
}


PHPAPI zval *php_domobject_new(xmlNodePtr obj, int *found, zval *wrapper_in  TSRMLS_DC)
{
	zval *wrapper;
	char *content;
	int rsrc_type;

		*found = 0;

	if (!obj) {
		if(!wrapper_in)
		{
			MAKE_STD_ZVAL(wrapper);
		}
		else
			wrapper = wrapper_in;
		ZVAL_NULL(wrapper);
		return wrapper;
	}

	if ((wrapper = (zval *) dom_object_get_data((void *) obj))) {
		zval_add_ref(&wrapper);
		*found = 1;
		return wrapper;
	}

	if(!wrapper_in)
	{
		MAKE_STD_ZVAL(wrapper);
	}
	else
		wrapper = wrapper_in;

	switch (Z_TYPE_P(obj)) {

		case XML_ELEMENT_NODE:
		{
			xmlNodePtr nodep = obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmlelement_class_entry);
			rsrc_type = le_domxmlelementp;
			add_property_long(wrapper, "type", Z_TYPE_P(nodep));
			add_property_stringl(wrapper, "tagname", (char *) nodep->name, strlen(nodep->name), 1);
			break;
		}

		case XML_TEXT_NODE:
		{
			xmlNodePtr nodep = obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmltext_class_entry);
			rsrc_type = le_domxmltextp;
			content = xmlNodeGetContent(nodep);
			add_property_long(wrapper, "type", Z_TYPE_P(nodep));
			add_property_stringl(wrapper, "name", "#text", 5, 1);
			if (content)
				add_property_stringl(wrapper, "content", (char *) content, strlen(content), 1);
			xmlFree(content);
			break;
		}

		case XML_COMMENT_NODE:
		{
			xmlNodePtr nodep = obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmlcomment_class_entry);
			rsrc_type = le_domxmlcommentp;
			content = xmlNodeGetContent(nodep);
			if (content) {
				add_property_long(wrapper, "type", Z_TYPE_P(nodep));
				add_property_stringl(wrapper, "name", "#comment", 8, 1);
				add_property_stringl(wrapper, "content", (char *) content, strlen(content), 1);
				xmlFree(content);
			}
			break;
		}

		case XML_PI_NODE:
		{
			xmlNodePtr nodep = obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmlpi_class_entry);
			rsrc_type = le_domxmlpip;
			content = xmlNodeGetContent(nodep);
			add_property_stringl(wrapper, "name", (char *) nodep->name, strlen(nodep->name), 1);
			if (content) {
				add_property_stringl(wrapper, "value", (char *) content, strlen(content), 1);
				xmlFree(content);
			}
			break;
		}

		case XML_ENTITY_REF_NODE:
		{
			xmlNodePtr nodep = obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmlentityref_class_entry);
			rsrc_type = le_domxmlentityrefp;
			add_property_stringl(wrapper, "name", (char *) nodep->name, strlen(nodep->name), 1);
			break;
		}

		case XML_ENTITY_DECL:
		case XML_ELEMENT_DECL:
		{
			xmlNodePtr nodep = obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmlnode_class_entry);
			rsrc_type = le_domxmlnodep;
			add_property_long(wrapper, "type", Z_TYPE_P(nodep));
			add_property_stringl(wrapper, "name", (char *) nodep->name, strlen(nodep->name), 1);
			if (Z_TYPE_P(obj) == XML_ENTITY_REF_NODE) {
				content = xmlNodeGetContent(nodep);
				if (content) {
					add_property_stringl(wrapper, "content", (char *) content, strlen(content), 1);
					xmlFree(content);
				}
			}
			break;
		}

		case XML_ATTRIBUTE_NODE:
		{
			xmlAttrPtr attrp = (xmlAttrPtr) obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmlattr_class_entry);
			rsrc_type = le_domxmlattrp;
			add_property_long(wrapper, "type", Z_TYPE_P(attrp));
			add_property_stringl(wrapper, "name", (char *) attrp->name, strlen(attrp->name), 1);
			content = xmlNodeGetContent((xmlNodePtr) attrp);
			if (content) {
				add_property_stringl(wrapper, "value", (char *) content, strlen(content), 1);
				xmlFree(content);
			}
			break;
		}

		case XML_DOCUMENT_NODE:
		case XML_HTML_DOCUMENT_NODE:
		{
			xmlDocPtr docp = (xmlDocPtr) obj;

			if(!wrapper_in)
				object_init_ex(wrapper, domxmldoc_class_entry);
			rsrc_type = le_domxmldocp;
			if (docp->name)
				add_property_stringl(wrapper, "name", (char *) docp->name, strlen(docp->name), 1);
			else
				add_property_stringl(wrapper, "name", "#document", 9, 1);
			if (docp->URL)
				add_property_stringl(wrapper, "url", (char *) docp->URL, strlen(docp->URL), 1);
			else
				add_property_stringl(wrapper, "url", "", 0, 1);
			if (docp->version)
				add_property_stringl(wrapper, "version", (char *) docp->version, strlen(docp->version), 1);
				else
				add_property_stringl(wrapper, "version", "", 0, 1);
			if (docp->encoding)
				add_property_stringl(wrapper, "encoding", (char *) docp->encoding, strlen(docp->encoding), 1);
			add_property_long(wrapper, "standalone", docp->standalone);
			add_property_long(wrapper, "type", Z_TYPE_P(docp));
			add_property_long(wrapper, "compression", docp->compression);
			add_property_long(wrapper, "charset", docp->charset);
			break;
		}

		/* FIXME: nodes of type XML_DTD_NODE used to be domxmldtd_class_entry.
		 * but the DOM Standard doesn't have a DomDtd class. The DocumentType
		 * class seems to be want we need and the libxml dtd functions are
		 * very much like the methods of DocumentType. I wonder what exactly
		 * is the difference between XML_DTD_NODE and XML_DOCUMENT_TYPE_NODE.
		 * Something like
		 * <!DOCTYPE chapter SYSTEM '/share/sgml/Norman_Walsh/db3xml10/db3xml10.dtd'
		 * [ <!ENTITY sp \"spanish\">
		 * ]>
		 * is considered a DTD by libxml, but from the DOM perspective it
		 * rather is a DocumentType
		 */
		case XML_DTD_NODE:
		case XML_DOCUMENT_TYPE_NODE:
		{
			xmlDtdPtr dtd = (xmlDtdPtr) obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmldoctype_class_entry);
/*			rsrc_type = le_domxmldtdp; */
			rsrc_type = le_domxmldoctypep;
/*			add_property_long(wrapper, "type", Z_TYPE_P(dtd)); */
			add_property_long(wrapper, "type", XML_DOCUMENT_TYPE_NODE);
			if (dtd->ExternalID)
				add_property_string(wrapper, "publicId", (char *) dtd->ExternalID, 1);
			else
				add_property_string(wrapper, "publicId", "", 1);
			if (dtd->SystemID)
				add_property_string(wrapper, "systemId", (char *) dtd->SystemID, 1);
			else
				add_property_string(wrapper, "systemId", "", 1);
			if (dtd->name)
				add_property_string(wrapper, "name", (char *) dtd->name, 1);
			break;
		}

		case XML_CDATA_SECTION_NODE:
		{
			xmlNodePtr nodep = obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmlcdata_class_entry);
			rsrc_type = le_domxmlcdatap;
			content = xmlNodeGetContent(nodep);
			add_property_long(wrapper, "type", Z_TYPE_P(nodep));
			if (content) {
				add_property_stringl(wrapper, "content", (char *) content, strlen(content), 1);
				xmlFree(content);
			}
			break;
		}

		case XML_NAMESPACE_DECL:
		{
			xmlNodePtr nodep = obj;
			if(!wrapper_in)
				object_init_ex(wrapper, domxmlns_class_entry);
			rsrc_type = le_domxmlnamespacep;
			add_property_long(wrapper, "type", Z_TYPE_P(nodep));
			add_property_stringl(wrapper, "name", (char *) nodep->name, strlen(nodep->name), 1);
			content = xmlNodeGetContent(nodep->children);
			if (content) {
				add_property_stringl(wrapper, "value", (char *) content, strlen(content), 1);
				xmlFree(content);
			}
			break;
		}

		default:
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported node type: %d\n", Z_TYPE_P(obj));
			FREE_ZVAL(wrapper);
			return NULL;
	}

	php_dom_set_object(wrapper, (void *) obj, rsrc_type TSRMLS_CC);
	return (wrapper);
}

static void domxml_error(void *ctx, const char *msg, ...)
{
	char buf[1024];
	va_list ap;
	TSRMLS_FETCH();

	va_start(ap, msg);
	vsnprintf(buf, 1024, msg, ap);
	va_end(ap);
	php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", buf);
}

static void domxml_error_ext(void *ctx, const char *msg, ...)
{
	xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx;
	xmlParserInputPtr input = NULL;
	char buf[1024];
	va_list ap;
	TSRMLS_FETCH();

	va_start(ap, msg);
	vsnprintf(buf, 1024, msg, ap);
	va_end(ap);

	if (ctxt != NULL && ctxt->_private != NULL) {
		zval *errormessages;
	   	MAKE_STD_ZVAL(errormessages);

		if(array_init(errormessages) != SUCCESS) {
			/* do error handling here */
		} 
		add_assoc_string(errormessages,"errormessage",buf,1);				
		input = ctxt->input;
		if (ctxt->name) {
			add_assoc_string(errormessages,"nodename",(char *) ctxt->name,1); 
		}
		if (input != NULL) {
			add_assoc_long(errormessages,"line",input->line);
			add_assoc_long(errormessages,"col",input->col);
			if (input->filename != NULL) {
				add_assoc_string(errormessages,"directory",(char *) input->directory,1);				
				add_assoc_string(errormessages,"file",(char *) input->filename,1);
			}
		}
		add_next_index_zval(ctxt->_private,errormessages);
	}
   	php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", buf);
	
}

static void domxml_error_validate(void *ctx, const char *msg, ...)
{
	domxml_ErrorCtxt *ctxt ;
	char buf[1024];
	va_list ap;
	TSRMLS_FETCH();

	va_start(ap, msg);
	vsnprintf(buf, 1024, msg, ap);
	va_end(ap);
	ctxt  = (domxml_ErrorCtxt*) ctx;

   	if (ctxt != NULL && ctxt->errors != NULL) {
		zval *errormessages;
	   	MAKE_STD_ZVAL(errormessages);
		if(array_init(errormessages) != SUCCESS) {
			/* do error handling here */
		} 
		if (ctxt->parser != NULL) {
			if (ctxt->parser->name) {
				add_assoc_string(errormessages,"nodename",(char *)ctxt->parser->name,1); 
			}

			if (ctxt->parser->input != NULL) {
			add_assoc_long(errormessages,"line",ctxt->parser->input->line);
			add_assoc_long(errormessages,"col",ctxt->parser->input->col);
				if (ctxt->parser->input->filename != NULL) {
					add_assoc_string(errormessages,"directory",(char *) ctxt->parser->input->directory,1);				
					add_assoc_string(errormessages,"file",(char *) ctxt->parser->input->filename,1);
				}
			}
		}
		
		if (ctxt->valid->node != NULL)
		{
			/*php_error_docref(NULL TSRMLS_CC, E_WARNING,"nodename %s",(char *) ctxt->valid->name);

			node = *ctxt->node;*/
		}
		add_assoc_string(errormessages,"errormessage",buf,1);			   
   		add_next_index_zval(ctxt->errors,errormessages);
	}

   	php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", buf);
	
}

PHP_RINIT_FUNCTION(domxml)
{
	xmlSetGenericErrorFunc(xmlGenericErrorContext, (xmlGenericErrorFunc)domxml_error);
#if HAVE_DOMXSLT
	xsltSetGenericErrorFunc(xsltGenericErrorContext, (xmlGenericErrorFunc)domxml_error);
#endif
	return SUCCESS;
}

PHP_RSHUTDOWN_FUNCTION(domxml)
{
	xmlSetGenericErrorFunc(xmlGenericErrorContext, NULL);
#if HAVE_DOMXSLT
	xsltSetGenericErrorFunc(xsltGenericErrorContext, NULL);
#endif
	return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(domxml)
{
#if HAVE_DOMXSLT
	xsltCleanupGlobals();
#endif
   	xmlCleanupParser();

/*	If you want do find memleaks in this module, compile libxml2 with --with-mem-debug and
	uncomment the following line, this will tell you the amount of not freed memory
	and the total used memory into apaches error_log  */
/*	xmlMemoryDump(); */

	return SUCCESS;
}

PHP_MINIT_FUNCTION(domxml)
{
	zend_class_entry ce;

	le_domxmlnodep = zend_register_list_destructors_ex(php_free_xml_node, NULL, "domnode", module_number);
	le_domxmlcommentp = zend_register_list_destructors_ex(php_free_xml_node, NULL, "domcomment", module_number);
	le_domxmltextp = zend_register_list_destructors_ex(php_free_xml_node, NULL, "domtext", module_number);
	le_domxmlattrp = zend_register_list_destructors_ex(php_free_xml_attr, NULL, "domattribute", module_number);
	le_domxmlelementp =	zend_register_list_destructors_ex(php_free_xml_node, NULL, "domelement", module_number);
	le_domxmldtdp = zend_register_list_destructors_ex(php_free_xml_node, NULL, "domdtd", module_number);
	le_domxmlcdatap = zend_register_list_destructors_ex(php_free_xml_node, NULL, "domcdata", module_number);
	le_domxmlentityrefp = zend_register_list_destructors_ex(php_free_xml_node, NULL, "domentityref", module_number);
	le_domxmlpip = zend_register_list_destructors_ex(php_free_xml_node, NULL, "dompi", module_number);
	le_domxmlparserp =	zend_register_list_destructors_ex(php_free_xml_parser, NULL, "domparser", module_number);
	le_domxmldoctypep = zend_register_list_destructors_ex(php_free_xml_node, NULL, "domdocumenttype", module_number);
	le_domxmlnamespacep = zend_register_list_destructors_ex(php_free_xml_node, NULL, "domnamespacenode", module_number);
	le_domxmldocp = zend_register_list_destructors_ex(php_free_xml_doc, NULL, "domdocument", module_number);
	/* Freeing the document contains freeing the complete tree.
	   Therefore nodes, attributes etc. may not be freed seperately.
	   Moved to end of list to support loading via dl()
	*/
	/* Not yet initialized le_*s */
	le_domxmlnotationp  = -10003;

#if defined(LIBXML_XPATH_ENABLED)
	le_xpathctxp = zend_register_list_destructors_ex(php_free_xpath_context, NULL, "xpathcontext", module_number);
	le_xpathobjectp = zend_register_list_destructors_ex(NULL, NULL, "xpathobject", module_number);
#endif

/*	le_domxmlnsp = register_list_destructors(NULL, NULL); */

#if HAVE_DOMXSLT
	le_domxsltstylesheetp =	zend_register_list_destructors_ex(php_free_xslt_stylesheet, NULL, "xsltstylesheet", module_number);
#endif

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domnode", php_domxmlnode_class_functions, NULL, NULL, NULL);
	domxmlnode_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domdocument", php_domxmldoc_class_functions, NULL, NULL, NULL);
	domxmldoc_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

   	INIT_OVERLOADED_CLASS_ENTRY(ce, "domparser", php_domxmlparser_class_functions, NULL, NULL, NULL);
	domxmlparser_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domdocumenttype", php_domxmldoctype_class_functions, NULL,	NULL, NULL);
	domxmldoctype_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "dtd", php_domxmldtd_class_functions, NULL, NULL, NULL);
	domxmldtd_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domelement", php_domxmlelement_class_functions, NULL, NULL, NULL);
	domxmlelement_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domattribute", php_domxmlattr_class_functions, NULL, NULL, NULL);
	domxmlattr_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domcdata", php_domxmlcdata_class_functions, NULL, NULL, NULL);
	domxmlcdata_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domtext", php_domxmltext_class_functions, NULL, NULL, NULL);
	domxmltext_class_entry = zend_register_internal_class_ex(&ce, domxmlcdata_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domcomment", php_domxmlcomment_class_functions, NULL, NULL, NULL);
	domxmlcomment_class_entry = zend_register_internal_class_ex(&ce, domxmlcdata_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domprocessinginstruction", php_domxmlpi_class_functions, NULL, NULL, NULL);
	domxmlpi_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domnotation", php_domxmlnotation_class_functions, NULL, NULL, NULL);
	domxmlnotation_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domentity", php_domxmlentity_class_functions, NULL, NULL, NULL);
	domxmlentity_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domentityreference", php_domxmlentityref_class_functions, NULL, NULL, NULL);
	domxmlentityref_class_entry = zend_register_internal_class_ex(&ce, domxmlnode_class_entry, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "domnamespace", php_domxmlns_class_functions, NULL, NULL, NULL);
	domxmlns_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);

#if defined(LIBXML_XPATH_ENABLED)
	INIT_OVERLOADED_CLASS_ENTRY(ce, "XPathContext", php_xpathctx_class_functions, NULL, NULL, NULL);
	xpathctx_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);

	INIT_OVERLOADED_CLASS_ENTRY(ce, "XPathObject", php_xpathobject_class_functions, NULL, NULL, NULL);
	xpathobject_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
#endif

#if HAVE_DOMXSLT
	INIT_OVERLOADED_CLASS_ENTRY(ce, "XsltStylesheet", php_domxsltstylesheet_class_functions, NULL, NULL, NULL);
	domxsltstylesheet_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
#endif

	REGISTER_LONG_CONSTANT("XML_ELEMENT_NODE",			XML_ELEMENT_NODE,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_NODE",		XML_ATTRIBUTE_NODE,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_TEXT_NODE",				XML_TEXT_NODE,				CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_CDATA_SECTION_NODE",	XML_CDATA_SECTION_NODE,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ENTITY_REF_NODE",		XML_ENTITY_REF_NODE,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ENTITY_NODE",			XML_ENTITY_NODE,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_PI_NODE",				XML_PI_NODE,				CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_COMMENT_NODE",			XML_COMMENT_NODE,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_DOCUMENT_NODE",			XML_DOCUMENT_NODE,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_DOCUMENT_TYPE_NODE",	XML_DOCUMENT_TYPE_NODE,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_DOCUMENT_FRAG_NODE",	XML_DOCUMENT_FRAG_NODE,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_NOTATION_NODE",			XML_NOTATION_NODE,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_HTML_DOCUMENT_NODE",	XML_HTML_DOCUMENT_NODE,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_DTD_NODE",				XML_DTD_NODE,				CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ELEMENT_DECL_NODE", 	XML_ELEMENT_DECL,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_DECL_NODE",	XML_ATTRIBUTE_DECL,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ENTITY_DECL_NODE",		XML_ENTITY_DECL,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_NAMESPACE_DECL_NODE",	XML_NAMESPACE_DECL,			CONST_CS | CONST_PERSISTENT);
#ifdef XML_GLOBAL_NAMESPACE
	REGISTER_LONG_CONSTANT("XML_GLOBAL_NAMESPACE",		XML_GLOBAL_NAMESPACE,		CONST_CS | CONST_PERSISTENT);
#endif
	REGISTER_LONG_CONSTANT("XML_LOCAL_NAMESPACE",		XML_LOCAL_NAMESPACE,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_CDATA",		XML_ATTRIBUTE_CDATA,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_ID",			XML_ATTRIBUTE_ID,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_IDREF",		XML_ATTRIBUTE_IDREF,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_IDREFS",		XML_ATTRIBUTE_IDREFS,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_ENTITY",		XML_ATTRIBUTE_ENTITIES,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_NMTOKEN",		XML_ATTRIBUTE_NMTOKEN,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_NMTOKENS",	XML_ATTRIBUTE_NMTOKENS,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_ENUMERATION",	XML_ATTRIBUTE_ENUMERATION,	CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XML_ATTRIBUTE_NOTATION",	XML_ATTRIBUTE_NOTATION,		CONST_CS | CONST_PERSISTENT);

#if defined(LIBXML_XPATH_ENABLED)
	REGISTER_LONG_CONSTANT("XPATH_UNDEFINED",			XPATH_UNDEFINED,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XPATH_NODESET",				XPATH_NODESET,				CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XPATH_BOOLEAN",				XPATH_BOOLEAN,				CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XPATH_NUMBER",				XPATH_NUMBER,				CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XPATH_STRING",				XPATH_STRING,				CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XPATH_POINT",				XPATH_POINT,				CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XPATH_RANGE",				XPATH_RANGE,				CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XPATH_LOCATIONSET",			XPATH_LOCATIONSET,			CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("XPATH_USERS",				XPATH_USERS,				CONST_CS | CONST_PERSISTENT);
#endif

	REGISTER_LONG_CONSTANT("DOMXML_LOAD_PARSING",		DOMXML_LOAD_PARSING,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("DOMXML_LOAD_VALIDATING",	DOMXML_LOAD_VALIDATING,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("DOMXML_LOAD_RECOVERING",	DOMXML_LOAD_RECOVERING,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("DOMXML_LOAD_SUBSTITUTE_ENTITIES",	DOMXML_LOAD_SUBSTITUTE_ENTITIES,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("DOMXML_LOAD_COMPLETE_ATTRS",DOMXML_LOAD_COMPLETE_ATTRS,		CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("DOMXML_LOAD_DONT_KEEP_BLANKS",DOMXML_LOAD_DONT_KEEP_BLANKS,		CONST_CS | CONST_PERSISTENT);

#if HAVE_DOMXSLT && HAVE_DOMEXSLT
	exsltRegisterAll();
#endif
	xmlInitParser();
	return SUCCESS;
}
/* }}} */

/* {{{ proto int domxml_test(int id)
   Unity function for testing */
PHP_FUNCTION(domxml_test)
{
	zval *id;

	if ((ZEND_NUM_ARGS() != 1) || getParameters(ht, 1, &id) == FAILURE)
		WRONG_PARAM_COUNT;

	convert_to_long(id);
	RETURN_LONG(Z_LVAL_P(id));
}
/* }}} */

/* {{{ PHP_MINFO_FUNCTION(domxml)
 */
PHP_MINFO_FUNCTION(domxml)
{
	/* don't know why that line was commented out in the previous version, so i left it (cmv) */
	php_info_print_table_start();
	php_info_print_table_row(2, "DOM/XML", "enabled");
	php_info_print_table_row(2, "DOM/XML API Version", DOMXML_API_VERSION);
/*	php_info_print_table_row(2, "libxml Version", LIBXML_DOTTED_VERSION); */
	php_info_print_table_row(2, "libxml Version", xmlParserVersion);
#if defined(LIBXML_HTML_ENABLED)
	php_info_print_table_row(2, "HTML Support", "enabled");
#endif
#if defined(LIBXML_XPATH_ENABLED)
	php_info_print_table_row(2, "XPath Support", "enabled");
#endif
#if defined(LIBXML_XPTR_ENABLED)
	php_info_print_table_row(2, "XPointer Support", "enabled");
#endif
#if HAVE_DOMXSLT
	{
	char buffer[128];
	int major, minor, subminor;

	php_info_print_table_row(2, "DOM/XSLT", "enabled");
/*	php_info_print_table_row(2, "libxslt Version", LIBXSLT_DOTTED_VERSION); */
	major = xsltLibxsltVersion/10000;
	minor = (xsltLibxsltVersion - major * 10000) / 100;
	subminor = (xsltLibxsltVersion - major * 10000 - minor * 100);
	snprintf(buffer, 128, "%d.%d.%d", major, minor, subminor);
	php_info_print_table_row(2, "libxslt Version", buffer);
	major = xsltLibxmlVersion/10000;
	minor = (xsltLibxmlVersion - major * 10000) / 100;
	subminor = (xsltLibxmlVersion - major * 10000 - minor * 100);
	snprintf(buffer, 128, "%d.%d.%d", major, minor, subminor);
	php_info_print_table_row(2, "libxslt compiled against libxml Version", buffer);
	}
#if HAVE_DOMEXSLT
	php_info_print_table_row(2, "DOM/EXSLT", "enabled");
	php_info_print_table_row(2, "libexslt Version", LIBEXSLT_DOTTED_VERSION);
#endif
#endif

	php_info_print_table_end();
}
/* }}} */

/* {{{ Methods of Class DomAttribute */

/* {{{ proto array domxml_attr_name(void)
   Returns list of attribute names
   Notice: domxml_node_name() does exactly the same for attribute-nodes,
           is this function here still needed, or would an alias be enough?
   */
PHP_FUNCTION(domxml_attr_name)
{
	zval *id;
	xmlAttrPtr attrp;

	DOMXML_GET_THIS_OBJ(attrp, id,le_domxmlattrp);

	DOMXML_NO_ARGS();

	RETURN_STRING((char *) (attrp->name), 1);
}
/* }}} */

/* {{{ proto array domxml_attr_value(void)
   Returns list of attribute names */
PHP_FUNCTION(domxml_attr_value)
{
	zval *id;
	xmlAttrPtr attrp;
	xmlChar *content;

	DOMXML_GET_THIS_OBJ(attrp, id, le_domxmlattrp);

	DOMXML_NO_ARGS();

	/* RETURN_STRING((char *) xmlNodeGetContent((xmlNodePtr) attrp), 1); */
	if ((content = xmlNodeGetContent((xmlNodePtr) attrp)) != NULL) {
		RETVAL_STRING(content,1);
	} else {
		RETURN_EMPTY_STRING();
	}
	xmlFree(content);
}
/* }}} */

/* {{{ proto bool domxml_attr_set_value(string content)
   Set value of attribute */
PHP_FUNCTION(domxml_attr_set_value)
{
	zval *id;
	xmlAttrPtr attrp;
	int content_len;
	char *content;

	DOMXML_PARAM_TWO(attrp, id, le_domxmlattrp, "s", &content, &content_len);

	/* 	If attribute has children unlink referenced nodes
		Spec indicates that content is to be overwritten and not appended
		xmlNodeSetContentLen will take care of removing and freeing the rest */
	if (attrp->children) {
		node_list_unlink(((xmlNodePtr) attrp) ->children);
	}
	xmlNodeSetContentLen((xmlNodePtr) attrp, content, content_len);
	RETURN_TRUE;

}
/* }}} */

/* {{{ proto array domxml_attr_specified(void)
   Returns list of attribute names */
PHP_FUNCTION(domxml_attr_specified)
{
	zval *id;
	xmlAttrPtr attrp;

	DOMXML_NOT_IMPLEMENTED();

	id = getThis();
	attrp = php_dom_get_object(id, le_domxmlattrp, 0 TSRMLS_CC);

	RETURN_TRUE;
}
/* }}} */

/* End of Methods DomAttr }}} */


/* {{{ Methods of Class DomProcessingInstruction */

/* {{{ proto array domxml_pi_target(void)
   Returns target of pi */
PHP_FUNCTION(domxml_pi_target)
{
	zval *id;
	xmlNodePtr nodep;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlpip);

	DOMXML_NO_ARGS();

	RETURN_STRING((char *) nodep->name, 1);
}
/* }}} */

/* {{{ proto array domxml_pi_data(void)
   Returns data of pi */
PHP_FUNCTION(domxml_pi_data)
{
	zval *id;
	xmlNodePtr nodep;
	xmlChar *content;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlpip);

	DOMXML_NO_ARGS();

	/* RETURN_STRING(xmlNodeGetContent(nodep), 1); */
	if ((content = xmlNodeGetContent(nodep)) != NULL) {
		RETVAL_STRING(content,1);
	} else {
		RETURN_EMPTY_STRING();
	}
	xmlFree(content);
}
/* }}} */

/* End of Methods of DomProcessingInstruction }}} */


/* {{{ Methods of Class DomCData */

/* {{{ proto array domxml_cdata_length(void)
   Returns list of attribute names */
PHP_FUNCTION(domxml_cdata_length)
{
	zval *id;
	xmlNodePtr nodep;

	DOMXML_NOT_IMPLEMENTED();

	id = getThis();
	nodep = php_dom_get_object(id, le_domxmlcdatap, 0 TSRMLS_CC);

	RETURN_LONG(1);
}
/* }}} */

/* End of Methods DomCDdata }}} */


/* {{{ Methods of Class DomNode */

/* {{{ proto object domxml_node(string name)
   Creates node */
PHP_FUNCTION(domxml_node)
{
	zval *rv = NULL;
	xmlNode *node;
	int ret, name_len;
	char *name;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len)  == FAILURE) {
		return;
	}

	node = xmlNewNode(NULL, name);
	if (!node) {
		RETURN_FALSE;
	}

	if(DOMXML_IS_TYPE(getThis(), domxmlnode_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), node, &ret);
	} else {
		DOMXML_RET_OBJ(rv, node, &ret);
	}
}
/* }}} */

/* {{{ proto object domxml_node_name(void)
   Returns name of node */
PHP_FUNCTION(domxml_node_name)
{
	zval *id;
	xmlNode *n;
	int fullQName = 0;
	xmlChar *qname = NULL;
	const char *str = NULL;

	DOMXML_PARAM_ONE(n, id, le_domxmlnodep,"|b",&fullQName);

	switch (Z_TYPE_P(n)) {
		case XML_ELEMENT_NODE:
			if (fullQName && n->ns && n->ns->prefix) {
				qname = xmlStrdup(n->ns->prefix);
				qname = xmlStrcat(qname, ":");
				qname = xmlStrcat(qname, n->name);
				str = qname;
			} else {
				str = n->name;
			}
			break;
		case XML_NAMESPACE_DECL:
			if (n->ns != NULL && n->ns->prefix) {
				qname = xmlStrdup("xmlns");
				qname = xmlStrcat(qname, ":");
				qname = xmlStrcat(qname, n->name);
				str = qname;
			} else {
				str = (char *) n->name;
			}
			break;

		case XML_TEXT_NODE:
			str = "#text";
			break;

		case XML_ATTRIBUTE_NODE:
			str = n->name;
			break;

		case XML_CDATA_SECTION_NODE:
			str = "#cdata-section";
			break;

		case XML_ENTITY_REF_NODE:
			str = n->name;
			break;

		case XML_ENTITY_NODE:
			str = NULL;
			break;

		case XML_PI_NODE:
			str = n->name;
			break;

		case XML_COMMENT_NODE:
			str = "#comment";
			break;

		case XML_DOCUMENT_NODE:
			str = "#document";
			break;

		case XML_DOCUMENT_FRAG_NODE:
			str = "#document-fragment";
			break;

		default:
			str = NULL;
			break;
	}

	if(str != NULL) {
		RETVAL_STRING((char *) str, 1);
		if (qname) {
			xmlFree(qname);
		}
	} else {
		RETURN_EMPTY_STRING();
	}
}
/* }}} */

/* {{{ proto object domxml_node_value(void)
   Returns name of value */
PHP_FUNCTION(domxml_node_value)
{
	zval *id;
	xmlNode *n;
	char *str = NULL;

	DOMXML_GET_THIS_OBJ(n, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	switch (Z_TYPE_P(n)) {
		case XML_TEXT_NODE:
		case XML_COMMENT_NODE:
		case XML_CDATA_SECTION_NODE:
		case XML_PI_NODE:
			str = n->content;
			break;
		case XML_NAMESPACE_DECL:
			if (n->children) {
				str = n->children->content;
			}
			break;
		default:
			str = NULL;
			break;
	}
	if(str != NULL) {
		RETURN_STRING((char *) str, 1);
	} else {
		RETURN_EMPTY_STRING();
	}
}
/* }}} */

/* {{{ proto bool domxml_is_blank_node(void)
   Returns true if node is blank */
PHP_FUNCTION(domxml_is_blank_node)
{
	zval *id;
	xmlNode *n;

	DOMXML_GET_THIS_OBJ(n, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	if(xmlIsBlankNode(n)) {
		RETURN_TRUE;
	} else {
		RETURN_FALSE;
	}
}
/* }}} */

/* {{{ proto int domxml_node_type(void)
   Returns the type of the node */
PHP_FUNCTION(domxml_node_type)
{
	zval *id;
	xmlNode *node;

	DOMXML_GET_THIS_OBJ(node, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	RETURN_LONG(node->type);
}
/* }}} */

/* {{{ proto object domxml_clone_node([bool deep])
   Clones a node */
PHP_FUNCTION(domxml_clone_node)
{
	zval *rv = NULL;
	zval *id;
	xmlNode *n, *node;
	int ret; 
	long recursive = 0;

	DOMXML_GET_THIS_OBJ(n, id, le_domxmlnodep);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &recursive) == FAILURE) {
		return;
	}

	node = xmlCopyNode(n, recursive);
	if (!node) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, node, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_first_child(void)
   Returns first child from list of children */
PHP_FUNCTION(domxml_node_first_child)
{
	zval *id, *rv = NULL;
	xmlNode *nodep, *first;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	first = nodep->children;
	if (!first) {
		return;
	}

	DOMXML_RET_OBJ(rv, first, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_last_child(void)
   Returns last child from list of children */
PHP_FUNCTION(domxml_node_last_child)
{
	zval *id, *rv = NULL;
	xmlNode *nodep, *last;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	last = nodep->last;
	if (!last) {
		return;
	}

	DOMXML_RET_OBJ(rv, last, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_next_sibling(void)
   Returns next child from list of children */
PHP_FUNCTION(domxml_node_next_sibling)
{
	zval *id, *rv = NULL;
	xmlNode *nodep, *first;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	first = nodep->next;
	if (!first) {
		rv = NULL;
		return;
	}

	DOMXML_RET_OBJ(rv, first, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_previous_sibling(void)
   Returns previous child from list of children */
PHP_FUNCTION(domxml_node_previous_sibling)
{
	zval *id, *rv = NULL;
	xmlNode *nodep, *first;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	first = nodep->prev;
	if (!first) {
		rv = NULL;
		return;
	}

	DOMXML_RET_OBJ(rv, first, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_owner_document(void)
   Returns document this node belongs to */
PHP_FUNCTION(domxml_node_owner_document)
{
	zval *id, *rv = NULL;
	xmlNode *nodep;
	xmlDocPtr docp;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	docp = nodep->doc;
	if (!docp) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, (xmlNodePtr) docp, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_has_child_nodes(void)
   Returns true if node has children */
PHP_FUNCTION(domxml_node_has_child_nodes)
{
	zval *id;
	xmlNode *nodep;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	if (nodep->children) {
		RETURN_TRUE;
	} else {
		RETURN_FALSE;
	}
}
/* }}} */

/* {{{ proto object domxml_node_has_attributes(void)
   Returns true if node has attributes */
PHP_FUNCTION(domxml_node_has_attributes)
{
	zval *id;
	xmlNode *nodep;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	if (Z_TYPE_P(nodep) != XML_ELEMENT_NODE)
		RETURN_FALSE;

	if (nodep->properties) {
		RETURN_TRUE;
	} else {
		RETURN_FALSE;
	}
}
/* }}} */

/* {{{ proto string domxml_node_prefix(void)
   Returns namespace prefix of node */
PHP_FUNCTION(domxml_node_prefix)
{
	zval *id;
	xmlNode *nodep;
	xmlNsPtr ns;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	ns = nodep->ns;
	if (!ns) {
		RETURN_EMPTY_STRING();
	}

	if (ns->prefix) {
		RETURN_STRING((char *) (ns->prefix), 1);
	} else {
		RETURN_EMPTY_STRING();
	}
}
/* }}} */

/* {{{ proto string domxml_node_namespace_uri(void)
   Returns namespace uri of node */
PHP_FUNCTION(domxml_node_namespace_uri)
{
	zval *id;
	xmlNode *nodep;
	xmlNsPtr ns;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	ns = nodep->ns;
	if (!ns) {
		/* return NULL if no ns is given...*/
		return;
	}

	if (ns->href) {
		RETURN_STRING((char *) (ns->href), 1);
	} else {
		RETURN_EMPTY_STRING();
	}
}
/* }}} */


/* {{{ proto object domxml_node_parent(void)
   Returns parent of node */
PHP_FUNCTION(domxml_node_parent)
{
	zval *id, *rv = NULL;
	xmlNode *nodep, *last;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	DOMXML_NO_ARGS();

	last = nodep->parent;
	if (!last) {
		return;
	}

	DOMXML_RET_OBJ(rv, last, &ret);
}
/* }}} */

/* {{{ proto array domxml_node_children(void)
   Returns list of children nodes */
PHP_FUNCTION(domxml_node_children)
{
	zval *id;
	xmlNode *nodep, *last;
	int ret;

	DOMXML_PARAM_NONE(nodep, id, le_domxmlnodep);

	/* Even if the nodep is a XML_DOCUMENT_NODE the type is at the
	   same position.
	 */
	if ((Z_TYPE_P(nodep) == XML_DOCUMENT_NODE) || (Z_TYPE_P(nodep) == XML_HTML_DOCUMENT_NODE))
		last = ((xmlDoc *) nodep)->children;
	else
		last = nodep->children;

	if (array_init(return_value) == FAILURE) {
		RETURN_FALSE;
	}
	
	if (last) {
		while (last) {
			zval *child;
			child = php_domobject_new(last, &ret, NULL TSRMLS_CC);
			add_next_index_zval(return_value, child);
			last = last->next;
		}
	}
}
/* }}} */

/* {{{ proto void domxml_node_unlink_node([object node])
   Deletes the node from tree, but not from memory*/
PHP_FUNCTION(domxml_node_unlink_node)
{
	zval *id;
	xmlNode *nodep;

	DOMXML_PARAM_NONE(nodep, id, le_domxmlnodep);

	xmlUnlinkNode(nodep);
	/*	This causes a Segmentation Fault for some reason.  Removing
		it allows the user to re-add the node at some other time, in
		addition to fixing the segfault.  Node will be freed at
		shutdown. */
	/*xmlFreeNode(nodep);
	zval_dtor(id);*/			/* This is not enough because the children won't be deleted */
}
/* }}} */

/* {{{ proto object domxml_node_replace_node(object domnode)
   Replaces one node with another node */
PHP_FUNCTION(domxml_node_replace_node)
{
	zval *id, *rv = NULL, *node;
	xmlNodePtr repnode, nodep, old_repnode;
	xmlDocPtr tmpdoc;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &node) == FAILURE) {
		return;
	}

	DOMXML_GET_OBJ(repnode, node, le_domxmlnodep);

	tmpdoc = repnode->doc;

	old_repnode = xmlReplaceNode(nodep, repnode);

	/* ReplaceNode will change the doc for only the first node 
	so check if doc was changed */
	if (tmpdoc != repnode->doc) {
		/* Set doc back to old doc otherwise libxml wont change all sub nodes */
		repnode->doc = tmpdoc;
		xmlSetTreeDoc(repnode, old_repnode->doc);
	}

	DOMXML_RET_OBJ(rv, old_repnode, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_append_child(object domnode)
   Adds node to list of children */
PHP_FUNCTION(domxml_node_append_child)
{
	zval *id, *rv = NULL, *node;
	xmlNodePtr child, parent, new_child = NULL;
	int ret;

	DOMXML_PARAM_ONE(parent, id, le_domxmlnodep, "o", &node);
	
	DOMXML_GET_OBJ(child, node, le_domxmlnodep);

	/* XXX:ls */
	if (child == parent) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't append node to itself");
		RETURN_FALSE;
	}
	
	if (!(child->doc == NULL || child->doc == parent->doc)) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't append node, which is in a different document than the parent node");
		RETURN_FALSE;
	}
	
	/* first unlink node, if child is already in the tree */
	if (child->doc == parent->doc && child->parent != NULL){
		xmlUnlinkNode(child);
	}
	
	/*
	 * The following code is from libxml2/tree.c and a fix for bug #20209
	 * libxml does free textnodes, if there are adjacent TEXT nodes
	 * This is bad behaviour for domxml, since then we have have reference
	 * to undefined nodes. The idea here is, that we do this text comparison
	 * by ourself and not free the nodes. and only if libxml2 won't do any harm
	 * call the function from libxml2.
	 * The code is exactly the same as in libxml2, only xmlFreeNode was taken away.
	 */

	if (child->type == XML_TEXT_NODE) {
		if ((parent->type == XML_TEXT_NODE) &&
			(parent->content != NULL)) {
			xmlNodeAddContent(parent, child->content);
			new_child = parent;
		}
		if ((parent->last != NULL) && (parent->last->type == XML_TEXT_NODE) &&
			(parent->last->name == child->name)) {
			xmlNodeAddContent(parent->last, child->content);
			new_child = parent->last;
		}
	}
	/* end libxml2 code */
	else if (child->type == XML_ATTRIBUTE_NODE) {
		if (parent->type != XML_ELEMENT_NODE) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Element node required to append Attribute");
			RETURN_FALSE;
		}
		if (parent->properties != NULL) {
			/* Check if an attribute with the same name exists */
			xmlAttrPtr foundattrp;
			if (child->ns == NULL)
				foundattrp = xmlHasProp(parent, child->name);
			else
				foundattrp = xmlHasNsProp(parent, child->name, child->ns->href);
			if (foundattrp != (xmlAttrPtr) child) {
				if (foundattrp != NULL) {
					if (dom_object_get_data((xmlNodePtr) foundattrp) == NULL) {
						node_list_unlink(foundattrp->children);
						xmlUnlinkNode((xmlNodePtr) foundattrp);
						xmlFreeProp(foundattrp);
					} else {
						xmlUnlinkNode((xmlNodePtr) foundattrp);
					}
					new_child = (xmlNodePtr) xmlCopyProp(parent, (xmlAttrPtr) child);
					if (!new_child) {
						php_error_docref(NULL TSRMLS_CC, E_WARNING, "No such attribute '%s'", child->name);
						RETURN_FALSE;
					} else {
						xmlAttr *prop;
						prop = parent->properties;
						while (prop->next != NULL) {
							prop = prop->next;
						}
						prop->next = (xmlAttrPtr) new_child;
						((xmlAttrPtr) new_child)->prev = prop;
					}
				}
			} else {
				new_child = (xmlNodePtr) foundattrp;
			}
		}
		/* For all other intents and purposes fall through to the xmlAddChild
		 * call
		 */
	}
	
	if (NULL == new_child) {
		new_child = xmlAddChild(parent, child);
	}

	if (NULL == new_child) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't append node");
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, new_child, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_append_sibling(object domnode)
   Adds node to list of siblings */
PHP_FUNCTION(domxml_node_append_sibling)
{
	zval *id, *rv = NULL, *node;
	xmlNodePtr child, nodep, new_child;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &node) == FAILURE) {
		return;
	}

	DOMXML_GET_OBJ(child, node, le_domxmlnodep);

	if (child->type == XML_ATTRIBUTE_NODE) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't append attribute node");
		RETURN_FALSE;
	}

	if (NULL == (new_child = xmlCopyNode(child, 1))) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to clone node");
		RETURN_FALSE;
	}

	/* FIXME reverted xmlAddChildList; crashes */
	child = xmlAddSibling(nodep, new_child);

	if (NULL == child) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't append node");
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, child, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_insert_before(object newnode, object refnode)
   Adds node in list of nodes before given node */
PHP_FUNCTION(domxml_node_insert_before)
{
	zval *id, *rv = NULL, *node, *ref;
	xmlNodePtr child, new_child, parent, refp;
	int ret;

	DOMXML_PARAM_TWO(parent, id, le_domxmlnodep, "oo!", &node, &ref);

	DOMXML_GET_OBJ(child, node, le_domxmlnodep);

	new_child = NULL;

	if (ref != NULL) {
		DOMXML_GET_OBJ(refp, ref, le_domxmlnodep);
		
   		 /* 
   		  * The following code is from libxml2/tree.c 
   		  * libxml does free textnodes, if there are adjacent TEXT nodes
   		  * This is bad behaviour for domxml, since then we have have reference
   		  * to undefined nodes. The idea here is, that we do this text comparison
   		  * by ourself and not free the nodes. and only if libxml2 won't do any harm
   		  * call the function from libxml2.
   		  * The code is exactly the same as in libxml2, only xmlFreeNode was taken away.
   		  */

		if (child->type == XML_TEXT_NODE) {
			if (refp->type == XML_TEXT_NODE) {
				xmlChar *tmp;

				tmp = xmlStrdup(child->content);
				tmp = xmlStrcat(tmp, refp->content);
				xmlNodeSetContent(refp, tmp);
				xmlFree(tmp);
				new_child = refp;
			}
			if ((refp->prev != NULL) && (refp->prev->type == XML_TEXT_NODE)
				&& (refp->name == refp->prev->name)) {
				xmlNodeAddContent(refp->prev, child->content);
				new_child = refp->prev;
			}
		}

		if (new_child == NULL)
			new_child = xmlAddPrevSibling(refp, child);
	} else {
		if (child->parent != NULL){
			xmlUnlinkNode(child);
		}
		new_child = xmlAddChild(parent, child);
	}
		

	if (NULL == new_child) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Couldn't add newnode as the previous sibling of refnode");
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, new_child, &ret);
}
/* }}} */

/* {{{ proto object domxml_node_remove_child(object domnode)
   Removes node from list of children */
PHP_FUNCTION(domxml_node_remove_child)
{
	zval *id, *node;
	xmlNodePtr children, child, nodep;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &node) == FAILURE) {
		return;
	}

	DOMXML_GET_OBJ(child, node, le_domxmlnodep);

	children = nodep->children;
	if (!children) {
		RETURN_FALSE;
	}

	while (children) {
		if (children == child) {
			zval *rv = NULL;
			xmlUnlinkNode(child);
			DOMXML_RET_OBJ(rv, child, &ret);
			return;
		}
		children = children->next;
	}
	RETURN_FALSE
}
/* }}} */

/* {{{ proto object domxml_node_replace_child(object newnode, object oldnode)
   Replaces node in list of children */
PHP_FUNCTION(domxml_node_replace_child)
{
	zval *id, *newnode, *oldnode;
	xmlNodePtr children, newchild, oldchild, nodep;
	int foundoldchild = 0;
	int ret;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "oo", &newnode, &oldnode) == FAILURE) {
		return;
	}

	DOMXML_GET_OBJ(newchild, newnode, le_domxmlnodep);
	DOMXML_GET_OBJ(oldchild, oldnode, le_domxmlnodep);

	children = nodep->children;
	if (!children) {
		RETURN_FALSE;
	}

	/* check for the old child and wether the new child is already a child */
	while (children) {
		if (children == oldchild) {
			foundoldchild = 1;
		}
		children = children->next;
	}
	/* if the child to replace is existent and the new child isn't already
	 * a child, then do the replacement
	 */
	if (foundoldchild) {
		zval *rv = NULL;
		xmlNodePtr node;
		node = xmlReplaceNode(oldchild, newchild);
		DOMXML_RET_OBJ(rv, oldchild, &ret);
		return;
	} else {
		RETURN_FALSE;
	}
}
/* }}} */

/* {{{ proto bool domxml_node_set_name(string name)
   Sets name of a node */
PHP_FUNCTION(domxml_node_set_name)
{
	zval *id;
	xmlNode *nodep;
	int name_len;
	char *name;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
		return;
	}

	xmlNodeSetName(nodep, name);

	RETURN_TRUE;
}
/* }}} */

/* {{{ proto array domxml_node_attributes(void)
   Returns list of attributes of node */
PHP_FUNCTION(domxml_node_attributes)
{
	zval *id, *attrs;
	xmlNode *nodep;
	int ret;

	DOMXML_PARAM_NONE(nodep, id, le_domxmlnodep);
	ret = node_attributes(&attrs, nodep TSRMLS_CC);
	if ( ret == -1) {
		RETURN_NULL();
	}

	if ( ret > -1) {
		*return_value = *attrs;
		FREE_ZVAL(attrs);
	} 
}
/* }}} */

/* {{{ proto object domxml_node_new_child(string name, string content)
   Adds child node to parent node */
PHP_FUNCTION(domxml_node_new_child)
{
	zval *id, *rv = NULL;
	xmlNodePtr child, nodep;
	int ret, name_len, content_len;
	char *name, *content = NULL;

	DOMXML_PARAM_FOUR(nodep, id, le_domxmlnodep, "s|s", &name, &name_len, &content, &content_len);

	child = xmlNewChild(nodep, NULL, name, content);

	if (!child) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, child, &ret);
}
/* }}} */

/* {{{ proto bool domxml_node_set_content(string content)
   Sets content of a node */
PHP_FUNCTION(domxml_node_set_content)
{
	zval *id;
	xmlNode *nodep;
	int content_len;
	char *content;

	DOMXML_PARAM_TWO(nodep, id, le_domxmlnodep, "s", &content, &content_len);

	/* FIXME: another gotcha. If node has children, calling
	 * xmlNodeSetContent will remove the children -> we loose the zval's
	 * To prevent crash, append content if children are set
	 */
	if (nodep->children) {
		xmlNodeAddContentLen(nodep, content, content_len);
	} else {
		xmlNodeSetContentLen(nodep, content, content_len);
	}

	/* FIXME: Actually the property 'content' of the node has to be updated
	   as well. Since 'content' should disappear sooner or later and being
	   replaces by a function 'content()' I skip this for now
	 */
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto string domxml_node_get_content()
   Gets content of a node.

   "Read the value of a node, this can be either the text carried directly by
this node if it's a TEXT node or the aggregate string of the values carried by
this node child's (TEXT and ENTITY_REF). Entity references are substituted."
   */
PHP_FUNCTION(domxml_node_get_content)
{
	zval *id;
	xmlNode *nodep;
	xmlChar *mem;

	DOMXML_PARAM_NONE(nodep, id, le_domxmlnodep);
	mem = xmlNodeGetContent(nodep);
	if (!mem) {
		RETURN_FALSE;
	}

	RETVAL_STRING(mem,1);
	xmlFree(mem);
}
/* }}} */


/* End of Methods DomNode }}} */


/* {{{ Methods of Class DomNotation */

/* {{{ proto string domxml_notation_public_id(void)
   Returns public id of notation node */
PHP_FUNCTION(domxml_notation_public_id)
{
	zval *id;
	xmlNotationPtr nodep;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnotationp);

	DOMXML_NO_ARGS();

	if(nodep->PublicID) {
		RETURN_STRING((char *) (nodep->PublicID), 1);
	} else {
		RETURN_EMPTY_STRING();
	}
}
/* }}} */

/* {{{ proto string domxml_notation_system_id(void)
   Returns system ID of notation node */
PHP_FUNCTION(domxml_notation_system_id)
{
	zval *id;
	xmlNotationPtr nodep;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnotationp);

	DOMXML_NO_ARGS();

	if(nodep->SystemID) {
		RETURN_STRING((char *) (nodep->SystemID), 1);
	} else {
		RETURN_EMPTY_STRING();
	}
}
/* }}} */

/* End of Methods DomNotation }}} */


/* {{{ Methods of Class DomElement */

/* {{{ proto object domxml_element(string name)
   Constructor of DomElement */
PHP_FUNCTION(domxml_element)
{
	zval *rv = NULL;
	xmlNode *node;
	int ret, name_len;
	char *name;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
		return;
	}

	node = xmlNewNode(NULL, name);
	if (!node) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, node, &ret);
}

/* }}} */

/* {{{ proto string domxml_elem_tagname(void)
   Returns tag name of element node */
PHP_FUNCTION(domxml_elem_tagname)
{
	zval *id;
	xmlNode *nodep;

	DOMXML_NO_ARGS();

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlelementp);

	DOMXML_NO_ARGS();

	RETURN_STRING((char *) (nodep->name), 1);
}
/* }}} */

/* {{{ proto string domxml_elem_get_attribute(string attrname)
   Returns value of given attribute */
PHP_FUNCTION(domxml_elem_get_attribute)
{
	zval *id;
	xmlNode *nodep;
	char *name, *value;
	int name_len;

	DOMXML_PARAM_TWO(nodep, id, le_domxmlelementp, "s", &name, &name_len);

	value = xmlGetProp(nodep, name);
	if (!value) {
		RETURN_EMPTY_STRING();
	} else {
		RETVAL_STRING(value, 1);
		xmlFree(value);
	}
}
/* }}} */

/* {{{ proto bool domxml_elem_set_attribute(string attrname, string value)
   Sets value of given attribute */
PHP_FUNCTION(domxml_elem_set_attribute)
{
	zval *id, *rv = NULL;
	xmlNode *nodep;
	xmlAttr *attr;
	int ret, name_len, value_len;
	char *name, *value;

	DOMXML_PARAM_FOUR(nodep, id, le_domxmlelementp, "ss", &name, &name_len, &value, &value_len);


	/*	If attribute exists, all children nodes are freed by setprop 
		unlink referenced children */
	attr = xmlHasProp(nodep,name);
	if (attr != NULL) {
		node_list_unlink(attr->children);
	}
	attr = xmlSetProp(nodep, name, value);
	if (!attr) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No such attribute '%s'", name);
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, (xmlNodePtr) attr, &ret);
}
/* }}} */

/* {{{ proto string domxml_elem_remove_attribute(string attrname)
   Removes given attribute */
PHP_FUNCTION(domxml_elem_remove_attribute)
{
	zval *id;
	xmlNode *nodep;
	xmlAttr *attrp;
	int name_len;
	char *name;

	DOMXML_PARAM_TWO(nodep, id, le_domxmlelementp, "s", &name, &name_len);
	attrp = xmlHasProp(nodep,name);
	if (attrp == NULL) {
		RETURN_FALSE;
	}

	/*	Check for registered nodes within attributes tree when attribute is not referenced  
		Unlink dependant nodes and free attribute if not registered */
	if (dom_object_get_data((xmlNodePtr) attrp) == NULL) {
		node_list_unlink(attrp->children);
		xmlUnlinkNode((xmlNodePtr) attrp);
		xmlFreeProp(attrp);
	} else {
		xmlUnlinkNode((xmlNodePtr) attrp);
	}

	RETURN_TRUE;
}
/* }}} */

/* {{{ proto string domxml_elem_get_attribute_node(string attrname)
   Returns value of given attribute */
PHP_FUNCTION(domxml_elem_get_attribute_node)
{
	zval *id, *rv = NULL;
	xmlNode *nodep;
	xmlAttr  *attrp;
	int name_len, ret;
	char *name;

	DOMXML_PARAM_TWO(nodep, id, le_domxmlelementp, "s", &name, &name_len);
	attrp = xmlHasProp(nodep,name);
	if (attrp == NULL) {
		RETURN_FALSE;
	}
	DOMXML_RET_OBJ(rv, (xmlNodePtr) attrp, &ret);
}
/* }}} */

/* {{{ proto bool domxml_elem_set_attribute_node(object attr)
   Sets value of given attribute */
/* since this function is not implemented, outcomment it for the time beeing
PHP_FUNCTION(domxml_elem_set_attribute_node)
{
	zval *id, *arg1, *rv = NULL;
	xmlNode *nodep;
	xmlAttr *attrp, *newattrp;
	int ret;

	if ((ZEND_NUM_ARGS() == 1) && getParameters(ht, 1, &arg1) == SUCCESS) {
		id = getThis();
		nodep = php_dom_get_object(id, le_domxmlelementp, 0 TSRMLS_CC);
		attrp = php_dom_get_object(arg1, le_domxmlattrp, 0 TSRMLS_CC);
	} else {
		WRONG_PARAM_COUNT;
	}

	FIXME: The following line doesn't work 
	newattrp = xmlCopyProp(nodep, attrp);
	if (!newattrp) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No such attribute '%s'", attrp->name);
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, (xmlNodePtr) newattrp, &ret);
}
*/
/* }}} */

/* {{{ proto string domxml_elem_has_attribute(string attrname)
   Checks for existenz given attribute */
PHP_FUNCTION(domxml_elem_has_attribute)
{
	zval *id;
	xmlNode *nodep;
	char *name, *value;
	int name_len;

	DOMXML_PARAM_TWO(nodep, id, le_domxmlelementp, "s", &name, &name_len);

	value = xmlGetProp(nodep, name);
	if (!value) {
		RETURN_FALSE;
	} else {
		xmlFree(value);
		RETURN_TRUE;
	}
}
/* }}} */

#if defined(LIBXML_XPATH_ENABLED)
/* {{{ proto string domxml_doc_get_elements_by_tagname(string tagname [,object xpathctx_handle] )
   Returns array with nodes with given tagname in document or empty array, if not found*/
PHP_FUNCTION(domxml_doc_get_elements_by_tagname)
{
	zval *id, *rv, *contextnode = NULL,*ctxpin = NULL;
	xmlDocPtr docp;
	xmlNode *contextnodep = NULL, *nodep = NULL;
	int name_len,i;
	char *name;
	xmlNodeSet *nodesetp = NULL;

	DOMXML_PARAM_FOUR(docp, id, le_domxmldocp, "s|oo", &name, &name_len,&ctxpin,&contextnode);

	if (contextnode) {
		DOMXML_GET_OBJ(contextnodep, contextnode, le_domxmlnodep);
		if (contextnodep->type == XML_ELEMENT_NODE) {
			nodep = contextnodep->children;
		}
	} else {
		nodep = xmlDocGetRootElement(docp);
	}

	MAKE_STD_ZVAL(rv);

	if(array_init(rv) != SUCCESS) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create required array");
		RETURN_FALSE;
	}

	nodesetp = php_get_elements_by_tagname(nodep, name, NULL);

	if(nodesetp) {
		for (i = 0; i < nodesetp->nodeNr; i++) {
			xmlNodePtr node = nodesetp->nodeTab[i];
			zval *child;
			int retnode;

			child = php_domobject_new(node, &retnode, NULL TSRMLS_CC);
			zend_hash_next_index_insert(Z_ARRVAL_P(rv), &child, sizeof(zval *), NULL);
		}
	}
	xmlXPathFreeNodeSet(nodesetp);
	*return_value = *rv;
	FREE_ZVAL(rv);
}
/* }}} */

typedef struct _idsIterator idsIterator;
struct _idsIterator {
	  xmlChar *elementId;
	    xmlNode *element;
};

static void idsHashScanner(void *payload, void *data, xmlChar *name)
{
	idsIterator *priv = (idsIterator *)data;

	if (priv->element == NULL && xmlStrEqual (name, priv->elementId))
		priv->element = ((xmlNode *)((xmlID *)payload)->attr)->parent;
}

/* {{{ proto string domxml_doc_get_element_by_id(string id)
   Returns element for given id or false if not found */
PHP_FUNCTION(domxml_doc_get_element_by_id)
{
	zval *id, *rv = NULL;
	xmlDocPtr docp;
	idsIterator iter;
	xmlHashTable *ids = NULL;
	int retnode,idname_len;
	char *idname;
	
	DOMXML_PARAM_TWO(docp, id, le_domxmldocp, "s", &idname, &idname_len);

	ids = (xmlHashTable *) docp->ids;
	if(ids) {
		iter.elementId = (xmlChar *) idname;
		iter.element = NULL;
		xmlHashScan(ids, (void *)idsHashScanner, &iter);
		rv = php_domobject_new(iter.element, &retnode, NULL TSRMLS_CC);
		SEPARATE_ZVAL(&rv);
		*return_value = *rv;
		FREE_ZVAL(rv);
	} else {
		RETURN_FALSE;
	}
}
/* }}} */
#endif

/* {{{ proto string domxml_elem_get_elements_by_tagname(string tagname)
   Returns array with nodes with given tagname in element or empty array, if not found */
PHP_FUNCTION(domxml_elem_get_elements_by_tagname)
{
	zval *id,*rv;
	xmlNode *nodep;
	int name_len,i;
	char *name;
	xmlNodeSet *nodesetp = NULL;

	DOMXML_PARAM_TWO(nodep, id, le_domxmlelementp, "s", &name, &name_len);

	MAKE_STD_ZVAL(rv);

	if(array_init(rv) != SUCCESS) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create required array");
		RETURN_FALSE;
	}

	nodesetp = php_get_elements_by_tagname(nodep->children, name, NULL);

	if(nodesetp) {
		for (i = 0; i < nodesetp->nodeNr; i++) {
			xmlNodePtr node = nodesetp->nodeTab[i];
			zval *child;
			int retnode;

			child = php_domobject_new(node, &retnode, NULL TSRMLS_CC);
			zend_hash_next_index_insert(Z_ARRVAL_P(rv), &child, sizeof(zval *), NULL);
		}
	}
	xmlXPathFreeNodeSet(nodesetp);
	*return_value = *rv;
	FREE_ZVAL(rv);

}
/* }}} */

/* End of Methods DomElement }}} */


/* {{{ Methods of Class DomDocumentType */

/* {{{ proto array domxml_doctype_name(void)
   Returns name of DocumentType */
PHP_FUNCTION(domxml_doctype_name)
{
	zval *id;
	xmlDtdPtr attrp;

	DOMXML_NO_ARGS();

	DOMXML_GET_THIS_OBJ(attrp, id, le_domxmldoctypep);

	RETURN_STRING((char *) (attrp->name), 1);
}
/* }}} */

/* {{{ proto array domxml_doctype_system_id(void)
   Returns system id of DocumentType */
PHP_FUNCTION(domxml_doctype_system_id)
{
	zval *id;
	xmlDtdPtr attrp;

	DOMXML_NO_ARGS();

	DOMXML_GET_THIS_OBJ(attrp, id, le_domxmldoctypep);

	if(attrp->SystemID) {
		RETURN_STRING((char *) (attrp->SystemID), 1);
	} else {
		RETURN_EMPTY_STRING();
	}
}
/* }}} */

/* {{{ proto array domxml_doctype_public_id(void)
   Returns public id of DocumentType */
PHP_FUNCTION(domxml_doctype_public_id)
{
	zval *id;
	xmlDtdPtr attrp;

	DOMXML_NO_ARGS();

	DOMXML_GET_THIS_OBJ(attrp, id, le_domxmldoctypep);

	if(attrp->ExternalID) {
		RETURN_STRING((char *) (attrp->ExternalID), 1);
	} else {
		RETURN_EMPTY_STRING();
	}
}
/* }}} */

/* {{{ proto array domxml_doctype_entities(void)
   Returns list of entities */
PHP_FUNCTION(domxml_doctype_entities)
{
	zval *id;
	xmlNode *last;
	xmlDtdPtr doctypep;
	int ret;

	DOMXML_NOT_IMPLEMENTED();

	DOMXML_PARAM_NONE(doctypep, id, le_domxmldoctypep);

	last = doctypep->entities;
	if (!last) {
		RETURN_FALSE;
	}

	if (array_init(return_value) == FAILURE) {
		RETURN_FALSE;
	}

	while (last) {
		zval *child;
		child = php_domobject_new(last, &ret, NULL TSRMLS_CC);
		add_next_index_zval(return_value, child);
		last = last->next;
	}
}
/* }}} */

/* {{{ proto array domxml_doctype_notations(void)
   Returns list of notations */
PHP_FUNCTION(domxml_doctype_notations)
{
	zval *id;
	xmlNode *last;
	xmlDtdPtr doctypep;
	int ret;

	DOMXML_NOT_IMPLEMENTED();

	DOMXML_PARAM_NONE(doctypep, id, le_domxmldoctypep);

	last = doctypep->notations;
	if (!last) {
		RETURN_FALSE;
	}

	if (array_init(return_value) == FAILURE) {
		RETURN_FALSE;
	}

	while (last) {
		zval *child;
		child = php_domobject_new(last, &ret, NULL TSRMLS_CC);
		add_next_index_zval(return_value, child);
		last = last->next;
	}
}
/* }}} */

/* End of Methods DomElementType }}} */


/* {{{ Methods of Class DomDocument */

/* {{{ proto object domxml_doc_doctype(void)
   Returns DomDocumentType */
PHP_FUNCTION(domxml_doc_doctype)
{
	zval *id, *rv = NULL;
	xmlDtdPtr dtd;
	xmlDocPtr docp;
	int ret;

	DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);

	DOMXML_NO_ARGS();

	dtd = xmlGetIntSubset(docp);
	if (!dtd) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, (xmlNodePtr) dtd, &ret);
}
/* }}} */

/* {{{ proto object domxml_doc_implementation(void)
   Returns DomeDOMImplementation */
PHP_FUNCTION(domxml_doc_implementation)
{
/*	zval *id;
	xmlDocPtr docp;*/

	DOMXML_NOT_IMPLEMENTED();

/*
	DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);

	rv = php_domobject_new(node, &ret TSRMLS_CC);
	SEPARATE_ZVAL(&rv);
	*return_value = *rv;
*/
}
/* }}} */

/* {{{ proto object domxml_doc_document_element(int domnode)
   Returns root node of document */
PHP_FUNCTION(domxml_doc_document_element)
{
	zval *id, *rv = NULL;
	xmlDoc *docp;
	xmlNode *root;
	int ret;

	DOMXML_PARAM_NONE(docp, id, le_domxmldocp);

	root = xmlDocGetRootElement(docp);
	if (!root) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, root, &ret);
}
/* }}} */

/* {{{ proto object domxml_doc_create_element(string name)
   Creates new element node */
PHP_FUNCTION(domxml_doc_create_element)
{
	zval *id, *rv = NULL;
	xmlNode *node;
	xmlDocPtr docp = NULL;
	int ret, name_len;
	char *name;

	if(!DOMXML_IS_TYPE(getThis(), domxmlelement_class_entry)) {
		DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
		return;
	}

	node = xmlNewNode(NULL, name);
	if (!node) {
		RETURN_FALSE;
	}
	node->doc = docp;

	if(DOMXML_IS_TYPE(getThis(), domxmlelement_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), node, &ret);
	} else {
		DOMXML_RET_OBJ(rv, node, &ret);
	}
}
/* }}} */

/* {{{ proto object domxml_doc_create_element_ns(string uri, string name [, string prefix])
   Creates new element node with a namespace */
PHP_FUNCTION(domxml_doc_create_element_ns)
{
	zval *id, *rv = NULL;
	xmlNode *node;
	xmlNs	*nsptr;
	xmlDocPtr docp = NULL;
	int ret, name_len, uri_len, prefix_len=0;
	char *name, *uri, *prefix;

	DOMXML_PARAM_SIX(docp, id, le_domxmldocp, "ss|s", &uri, &uri_len, &name, &name_len, &prefix, &prefix_len);

	nsptr = xmlSearchNsByHref(docp, xmlDocGetRootElement(docp), (xmlChar*) uri);
	node = xmlNewNode(nsptr, name);

	if (!node) {
		RETURN_FALSE;
	}
	/* if no namespace with the same uri was found, we have to create a new one.
	     I do this here with "a" + a random number. this is not very sophisticated,
		 therefore if someone has a better idea in creating unique prefixes, here's your
		 chance (a0,a1, etc would be good enough, this is the way mozilla does it). I'm
		 to lazy right now to think of a better solution... */
	
	if (nsptr == NULL) {
		/* if there was a prefix provided, take that, otherwise generate a new one
			 this is not w3c-like, since the have no option to provide a prefix, but
			 i don't care :)
		*/
		if (prefix_len == 0)
		{	
			char prefixtmp[20];
			int random;
			random = (int) (10000.0*php_rand(TSRMLS_C)/(PHP_RAND_MAX));
			sprintf(prefixtmp, "a%d", random);
			prefix = prefixtmp;
		}
		nsptr = xmlNewNs(node, uri, prefix);
		xmlSetNs(node, nsptr);
	}
	
	node->doc = docp;

	if(DOMXML_IS_TYPE(getThis(), domxmlelement_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), node, &ret);
	} else {
		DOMXML_RET_OBJ(rv, node, &ret);
	}
}
/* }}} */

/* {{{ proto bool domxml_node_add_namespace(string uri, string prefix)
   Adds a namespace declaration to a node */
PHP_FUNCTION(domxml_node_add_namespace)
{
	zval *id;
	xmlNode *nodep;
	xmlNs	*nsptr;
	int prefix_len, uri_len;
	char *prefix, *uri;

	DOMXML_PARAM_FOUR(nodep, id, le_domxmldocp, "ss", &uri, &uri_len, &prefix, &prefix_len);

	if (NULL == (nsptr = xmlNewNs(nodep,uri,prefix))) {
		RETURN_FALSE;
	} else {
		RETURN_TRUE;
	}
	
}
/* }}} */

/* {{{ proto void domxml_node_set_namespace(string uri [, string prefix])
   Sets the namespace of a node */
PHP_FUNCTION(domxml_node_set_namespace)
{
	zval *id;
	xmlNode *nodep;
	xmlNs	*nsptr;
	int prefix_len = 0, uri_len;
	char *prefix, *uri;

	DOMXML_PARAM_FOUR(nodep, id, le_domxmldocp, "s|s", &uri, &uri_len, &prefix, &prefix_len);

	/* if node is in a document, search for an already existing namespace */
	if (nodep->doc != NULL) {
		if (nodep->type == XML_ATTRIBUTE_NODE) {
			nsptr = xmlSearchNsByHref(nodep->doc, nodep->parent, (xmlChar*) uri);
		} else  {
			nsptr = xmlSearchNsByHref(nodep->doc, nodep, (xmlChar*) uri);
		} 
	} else {
		nsptr = NULL;
	}

	/* if no namespace decleration was found in the parents of the node, generate one */
	if (nsptr == NULL) {
		/* if there was a prefix provided, take that, otherwise generate a new one */
		if (prefix_len == 0) {	
			char prefixtmp[20];
			int random;
			random = (int) (10000.0*php_rand(TSRMLS_C)/(PHP_RAND_MAX));
			sprintf(prefixtmp, "a%d", random);
			prefix = prefixtmp;
		}
		if (nodep->type == XML_ATTRIBUTE_NODE) {
			nsptr = xmlNewNs(nodep->parent, uri, prefix);
		} else {
			nsptr = xmlNewNs(nodep, uri, prefix);
		}
	}

	xmlSetNs(nodep, nsptr);
}
/* }}} */

/* {{{ proto object domxml_doc_create_text_node(string content)
   Creates new text node */
PHP_FUNCTION(domxml_doc_create_text_node)
{
	zval *id, *rv = NULL;
	xmlNode *node;
	xmlDocPtr docp = NULL;
	int ret, content_len;
	char *content;

	if(!DOMXML_IS_TYPE(getThis(), domxmltext_class_entry)) {
		DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &content, &content_len) == FAILURE) {
		return;
	}

	node = xmlNewTextLen(content, content_len);
	if (!node) {
		RETURN_FALSE;
	}
	node->doc = docp;

	if(DOMXML_IS_TYPE(getThis(), domxmltext_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), node, &ret);
	} else {
		DOMXML_RET_OBJ(rv, node, &ret);
	}
}
/* }}} */

/* {{{ proto object domxml_doc_create_comment(string content)
   Creates new comment node */
PHP_FUNCTION(domxml_doc_create_comment)
{
	zval *id, *rv = NULL;
	xmlNode *node;
	xmlDocPtr docp = NULL;
	int ret, content_len;
	char *content;

	if(!DOMXML_IS_TYPE(getThis(), domxmlcomment_class_entry)) {
		DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &content, &content_len) == FAILURE) {
		return;
	}

	node = xmlNewComment(content);
	if (!node) {
		RETURN_FALSE;
	}
	node->doc = docp;

	if(DOMXML_IS_TYPE(getThis(), domxmlcomment_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), node, &ret);
	} else {
		DOMXML_RET_OBJ(rv, node, &ret);
	}
}
/* }}} */

/* {{{ proto object domxml_doc_create_attribute(string name, string value)
   Creates new attribute node */
PHP_FUNCTION(domxml_doc_create_attribute)
{
	zval *id, *rv = NULL;
	xmlAttrPtr node;
	xmlDocPtr docp = NULL;
	int ret, name_len, value_len;
	char *name, *value;

	if(!DOMXML_IS_TYPE(getThis(), domxmlattr_class_entry)) {
		DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &value, &value_len) == FAILURE) {
		return;
	}

	node = xmlNewProp(NULL, name, value);
	if (!node) {
		RETURN_FALSE;
	}
	node->doc = docp;

	if(DOMXML_IS_TYPE(getThis(), domxmlattr_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), (xmlNodePtr) node, &ret);
	} else {
		DOMXML_RET_OBJ(rv, (xmlNodePtr) node, &ret);
	}
}
/* }}} */

/* {{{ proto object domxml_doc_create_cdata_section(string content)
   Creates new cdata node */
PHP_FUNCTION(domxml_doc_create_cdata_section)
{
	zval *id, *rv = NULL;
	xmlNode *node;
	xmlDocPtr docp = NULL;
	int ret, content_len;
	char *content;

	if(!DOMXML_IS_TYPE(getThis(), domxmlcdata_class_entry)) {
		DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &content, &content_len) == FAILURE) {
		return;
	}

	node = xmlNewCDataBlock(docp, content, content_len);
	if (!node) {
		RETURN_FALSE;
	}
	node->doc = docp;

	if(DOMXML_IS_TYPE(getThis(), domxmlcdata_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), node, &ret);
	} else {
		DOMXML_RET_OBJ(rv, node, &ret);
	}
}
/* }}} */

/* {{{ proto object domxml_doc_create_entity_reference(string name)
   Creates new cdata node */
PHP_FUNCTION(domxml_doc_create_entity_reference)
{
	zval *id, *rv = NULL;
	xmlNode *node;
	xmlDocPtr docp = NULL;
	int ret, name_len;
	char *name;

	if(!DOMXML_IS_TYPE(getThis(), domxmlentityref_class_entry)) {
		DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
		return;
	}
	node = xmlNewReference(docp, name);
	if (!node) {
		RETURN_FALSE;
	}
	node->doc = docp;

	if(DOMXML_IS_TYPE(getThis(), domxmlentityref_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), node, &ret);
	} else {
		DOMXML_RET_OBJ(rv, node, &ret);
	}
}
/* }}} */

/* {{{ proto object domxml_doc_create_processing_instruction(string name)
   Creates new processing_instruction node */
PHP_FUNCTION(domxml_doc_create_processing_instruction)
{
	zval *id, *rv = NULL;
	xmlNode *node;
	xmlDocPtr docp = NULL;
	int ret, name_len, content_len;
	char *name, *content;

	if(!DOMXML_IS_TYPE(getThis(), domxmlpi_class_entry)) {
		DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &content, &content_len) == FAILURE) {
		return;
	}

	node = xmlNewPI(name, content);
	if (!node) {
		RETURN_FALSE;
	}
	node->doc = docp;

	if(DOMXML_IS_TYPE(getThis(), domxmlpi_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), node, &ret);
	} else {
		DOMXML_RET_OBJ(rv, node, &ret);
	}
}
/* }}} */

/* {{{ proto object domxml_doc_imported_node(object node, bool recursive)
   Creates new element node */
PHP_FUNCTION(domxml_doc_imported_node)
{
	zval *arg1, *id, *rv = NULL;
	xmlNodePtr node, srcnode;
	xmlDocPtr docp;
	int ret; 
	long recursive = 0;

	DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);

	/* FIXME: which object type to expect? */
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|l", &arg1, &recursive) == FAILURE) {
		return;
	}

	DOMXML_GET_OBJ(srcnode, arg1, le_domxmlnodep);

	/* node = xmlCopyNode(srcnode, recursive); */
	node = xmlDocCopyNode(srcnode, docp, recursive);
	if (!node) {
		RETURN_FALSE;
	}
	/* No longer need handled by xmlDocCopyNode
	node->doc = docp;
	*/

	DOMXML_RET_OBJ(rv, node, &ret);
}
/* }}} */

/* {{{ proto object domxml_dtd(void)
   Returns DTD of document */
PHP_FUNCTION(domxml_intdtd)
{
	zval *id, *rv = NULL;
	xmlDoc *docp;
	xmlDtd *dtd;
	int ret;

	DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);

	dtd = xmlGetIntSubset(docp);
	if (!dtd) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, (xmlNodePtr) dtd, &ret);
}
/* }}} */

/* {{{ proto string domxml_dump_mem(object doc_handle [, int format][, encoding])
   Dumps document into string and optionally formats it */
PHP_FUNCTION(domxml_dump_mem)
{
	zval *id;
	xmlDoc *docp;
	xmlChar *mem;
	int format = 0;
	int size, keepblanks;
	int encoding_len = 0;
	char *encoding;


	DOMXML_PARAM_THREE(docp, id, le_domxmldocp, "|ls", &format, &encoding, &encoding_len);
	if (format) {
		keepblanks = xmlKeepBlanksDefault(0);
		if (encoding_len) {
			xmlDocDumpFormatMemoryEnc(docp, &mem, &size, encoding, format);
		} else {
			xmlDocDumpFormatMemory(docp, &mem, &size, format);
		}
		xmlKeepBlanksDefault(keepblanks);
	} else {
		if (encoding_len) {
			xmlDocDumpMemoryEnc(docp, &mem, &size, encoding);
		} else {
			xmlDocDumpMemory(docp, &mem, &size);
		}
	}

	if (!size) {
		RETURN_FALSE;
	}
	RETVAL_STRINGL(mem, size, 1);
	xmlFree(mem);
}
/* }}} */

/* {{{ proto int domxml_dump_mem_file(string filename [, int compressmode [, int format]])
   Dumps document into file and uses compression if specified. Returns false on error, otherwise the length of the xml-document (uncompressed) */
PHP_FUNCTION(domxml_dump_mem_file)
{
	zval *id;
	xmlDoc *docp;
	int file_len, bytes, keepblanks;
	int format = 0;
	int compressmode = 0;
	char *file;

	DOMXML_PARAM_FOUR(docp, id, le_domxmldocp, "s|ll", &file, &file_len, &compressmode, &format);

	if ((PG(safe_mode) && (!php_checkuid(file, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(file TSRMLS_CC)) {
		RETURN_FALSE;
	}

	xmlSetCompressMode(compressmode);

	if (format) {
		keepblanks = xmlKeepBlanksDefault(0);
		bytes = xmlSaveFormatFile(file, docp, format);
		xmlKeepBlanksDefault(keepblanks);
	} else {
		bytes = xmlSaveFile(file, docp);
	}

	if (bytes == -1) {
		RETURN_FALSE;
	}
	RETURN_LONG(bytes);
}
/* }}} */

/* {{{ proto string domxml_dump_node(object doc_handle, object node_handle [, int format [, int level]])
   Dumps node into string */
PHP_FUNCTION(domxml_dump_node)
{
	zval *id, *nodep;
	xmlDocPtr docp;
	xmlNodePtr elementp;
	xmlChar *mem ;
	xmlBufferPtr buf;
	int level = 0;
	int format = 0;

	DOMXML_PARAM_THREE(docp, id, le_domxmldocp, "o|ll", &nodep, &format, &level);

	DOMXML_GET_OBJ(elementp, nodep, le_domxmlnodep);

	if (Z_TYPE_P(elementp) == XML_DOCUMENT_NODE || Z_TYPE_P(elementp) == XML_HTML_DOCUMENT_NODE ) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot dump element with a document node");
		RETURN_FALSE;
	}

	if (docp->type != XML_DOCUMENT_NODE && docp->type != XML_HTML_DOCUMENT_NODE) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Object has to be a DomDocument Node");
		RETURN_FALSE;
	}

	buf = xmlBufferCreate();
	if (!buf) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not fetch buffer");
		RETURN_FALSE;
	}

	xmlNodeDump(buf, docp, elementp, level, format);

	mem = (xmlChar*) xmlBufferContent(buf);

	if (!mem) {
		xmlBufferFree(buf);
		RETURN_FALSE;
	}
	RETVAL_STRING(mem,  1);

	xmlBufferFree(buf);

}
/* }}} */

/* {{{ idsHashScanner2(void *payload, void *data, xmlChar *name)
 */
static void idsHashScanner2(void *payload, void *data, xmlChar *name)
{
	zval *return_value = (zval *) data;
	zval *child;
	int ret;
	xmlNode *nodep;

	TSRMLS_FETCH();

	nodep = ((xmlNode *)((xmlID *)payload)->attr)->parent;
	child = php_domobject_new(nodep, &ret, NULL TSRMLS_CC);
	add_next_index_zval(return_value, child);
}
/* }}} */

/* {{{ proto string domxml_doc_ids(object doc_handle)
   Returns array of ids */
PHP_FUNCTION(domxml_doc_ids)
{
	zval *id;
	xmlDoc *docp;
	xmlHashTable *ids = NULL;

	DOMXML_GET_THIS_OBJ(docp, id, le_domxmldocp);

	ids = docp->ids;

	if(ids) {
		if (array_init(return_value) == FAILURE) {
			RETURN_FALSE;
		}

		xmlHashScan(ids, (void *)idsHashScanner2, return_value);
	} else {
		RETURN_FALSE;
	}
}
/* }}} */

static xmlDocPtr domxml_document_parser(int mode, int loadtype, char *source, void *data TSRMLS_DC) {
    xmlDocPtr ret;
    xmlParserCtxtPtr ctxt;
	domxml_ErrorCtxt errorCtxt;
	char *directory = NULL;
	int validate = 0, resolve_externals = 0;
	int keep_blanks = 1, recovery = 0, substitute_ent;

	substitute_ent = xmlSubstituteEntitiesDefaultValue;

	if (mode & DOMXML_LOAD_DONT_KEEP_BLANKS) 
		keep_blanks  =  0;

	if(mode & DOMXML_LOAD_SUBSTITUTE_ENTITIES)
		substitute_ent = 1;

	if(mode & DOMXML_LOAD_COMPLETE_ATTRS)
		resolve_externals = XML_COMPLETE_ATTRS;

	if(mode & DOMXML_LOAD_VALIDATING)
		validate = 1;

	if(mode & DOMXML_LOAD_RECOVERING)
		recovery = 1;

	xmlInitParser();

	keep_blanks = xmlKeepBlanksDefault(keep_blanks);

	if (loadtype == DOMXML_LOAD_FILE) {
		ctxt = xmlCreateFileParserCtxt(source);
	} else {
		ctxt = xmlCreateDocParserCtxt(source);
	}

	xmlKeepBlanksDefault(keep_blanks);
	/* xmlIndentTreeOutput default is changed in xmlKeepBlanksDefault
	reset back to 1 which is default value */

	xmlIndentTreeOutput = 1;

	if (ctxt == NULL) {
		return(NULL);
	}

	if (loadtype == DOMXML_LOAD_FILE) {
		if ((ctxt->directory == NULL) && (directory == NULL))
			directory = xmlParserGetDirectory(source);
		if ((ctxt->directory == NULL) && (directory != NULL))
			ctxt->directory = (char *) xmlStrdup((xmlChar *) directory);
	}

	ctxt->validate = validate;
	ctxt->loadsubset = resolve_externals;
	ctxt->replaceEntities = substitute_ent;

	/* Always set error handling as threaded libxml doesnt pick up the global handling 
	set by xmlSetGenericErrorFunc for child threads */
	ctxt->_private = data;
 	errorCtxt.valid = &ctxt->vctxt;
	errorCtxt.errors = data;
	errorCtxt.parser = ctxt;   

	ctxt->sax->error = domxml_error_ext;
	ctxt->sax->warning = domxml_error_ext;
#if LIBXML_VERSION >= 20600
	ctxt->sax->cdataBlock = NULL;
#endif
	ctxt->vctxt.userData= (void *) &errorCtxt;
	ctxt->vctxt.error    = (xmlValidityErrorFunc) domxml_error_validate;
	ctxt->vctxt.warning  = (xmlValidityWarningFunc) domxml_error_validate;

	xmlParseDocument(ctxt);

	if (ctxt->wellFormed || recovery)
		ret = ctxt->myDoc;
	else {
		ret = NULL;
		xmlFreeDoc(ctxt->myDoc);
		ctxt->myDoc = NULL;
	}

	xmlFreeParserCtxt(ctxt);

	return(ret);
}

/* {{{ proto object xmldoc(string xmldoc[, int mode[, array error]])
   Creates DOM object of XML document */
PHP_FUNCTION(xmldoc)
{
	zval *rv = NULL;
	xmlDoc *docp = NULL;
	int ret; 
	char *buffer;
	int buffer_len;
	long mode = 0;
	zval *errors ;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz", &buffer, &buffer_len, &mode, &errors) == FAILURE) {
		return;
	}

	if (ZEND_NUM_ARGS() == 3) {
		zval_dtor(errors);
		array_init(errors);
		docp = domxml_document_parser(mode, 0, buffer, errors TSRMLS_CC);
	} else {
		docp = domxml_document_parser(mode, 0, buffer, NULL TSRMLS_CC);
	}

	if (!docp)
		RETURN_FALSE;

	if(DOMXML_IS_TYPE(getThis(), domxmldoc_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), (xmlNodePtr) docp, &ret);
	} else {
		DOMXML_RET_OBJ(rv, (xmlNodePtr) docp, &ret);
	}
}
/* }}} */

/* {{{ proto object xmldocfile(string filename[, int mode[, array error])
   Creates DOM object of XML document in file */
PHP_FUNCTION(xmldocfile)
{
	zval *rv = NULL;
	xmlDoc *docp = NULL;
	int ret, file_len;
	char *file;
	long mode = 0;
	zval *errors = NULL;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz", &file, &file_len, &mode,  &errors) == FAILURE) {
		return;
	}

	if (ZEND_NUM_ARGS() == 3) {
		zval_dtor(errors);
		array_init(errors);
		docp = domxml_document_parser(mode, 1, file, errors TSRMLS_CC);
	} else {
		docp = domxml_document_parser(mode, 1, file, NULL TSRMLS_CC);
	}

	if (!docp) {
		RETURN_FALSE;
	}

	if(DOMXML_IS_TYPE(getThis(), domxmldoc_class_entry)) {
		DOMXML_DOMOBJ_NEW(getThis(), (xmlNodePtr) docp, &ret);
	} else {
		DOMXML_RET_OBJ(rv, (xmlNodePtr) docp, &ret);
	}

}
/* }}} */

#if defined(LIBXML_HTML_ENABLED)
/* {{{ proto string domxml_html_dump_mem([int doc_handle])
   Dumps document into string as HTML */
PHP_FUNCTION(domxml_html_dump_mem)
{
	zval *id;
	xmlDoc *docp;
	xmlChar *mem;
	int size;

	DOMXML_PARAM_NONE(docp, id, le_domxmldocp);

	htmlDocDumpMemory(docp, &mem, &size);
	if (!size) {
		if (mem)
			xmlFree(mem);
		RETURN_FALSE;
	}
	RETVAL_STRINGL(mem, size, 1);
	xmlFree(mem);
}
/* }}} */

/* {{{ proto object html_doc(string html_doc [, bool from_file])
   Creates DOM object of HTML document */
PHP_FUNCTION(html_doc)
{
	zval *rv = NULL;
	xmlDoc *docp;
	int ret;
	char *buffer;
	int buffer_len;
	zend_bool from_file = 0;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|b", &buffer, &buffer_len, &from_file) == FAILURE) {
		return;
	}

	if (from_file) {
		docp = htmlParseFile(buffer, NULL);
	} else {
		docp = htmlParseDoc(buffer, NULL);
	}
	if (!docp)
		RETURN_FALSE;

	DOMXML_RET_OBJ(rv, (xmlNodePtr) docp, &ret);
}
/* }}} */

/* {{{ proto object html_doc_file(string filename)
   Creates DOM object of HTML document in file */
PHP_FUNCTION(html_doc_file)
{
	zval *rv = NULL;
	xmlDoc *docp;
	int ret, file_len;
	char *file;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) {
		return;
	}

	docp = htmlParseFile(file, NULL);
	if (!docp) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, (xmlNodePtr) docp, &ret);

	add_property_resource(return_value, "doc", ret);
	if (docp->name)
		add_property_stringl(return_value, "name", (char *) docp->name, strlen(docp->name), 1);
	if (docp->URL)
		add_property_stringl(return_value, "url", (char *) docp->URL, strlen(docp->URL), 1);
	if (docp->version)
		add_property_stringl(return_value, "version", (char *) docp->version, strlen(docp->version), 1);
/*	add_property_stringl(return_value, "version", (char *) docp->version, strlen(docp->version), 1);*/
	if (docp->encoding)
		add_property_stringl(return_value, "encoding", (char *) docp->encoding, strlen(docp->encoding), 1);
	add_property_long(return_value, "standalone", docp->standalone);
	add_property_long(return_value, "type", Z_TYPE_P(docp));
	add_property_long(return_value, "compression", docp->compression);
	add_property_long(return_value, "charset", docp->charset);
	zend_list_addref(ret);
}
/* }}} */
#endif  /* defined(LIBXML_HTML_ENABLED) */

/* {{{ proto bool domxml_substitute_entities_default(bool enable)
   Set and return the previous value for default entity support */
PHP_FUNCTION(domxml_substitute_entities_default)
{
	zend_bool enable;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &enable) == FAILURE) {
		return;
	}

	RETURN_BOOL(xmlSubstituteEntitiesDefault(enable));
}
/* }}} */

/* {{{ proto bool domxml_node_text_concat(string content)
   Add string tocontent of a node */
PHP_FUNCTION(domxml_node_text_concat)
{
	zval *id;
	xmlNode *nodep;
	char *content;
	int content_len;

	DOMXML_GET_THIS_OBJ(nodep, id, le_domxmlnodep);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &content, &content_len) == FAILURE) {
		return;
	}

	if (content_len)
		xmlTextConcat(nodep, content, content_len);

	RETURN_TRUE;
}
/* }}} */

/* {{{ proto object domxml_add_root(string name)
   Adds root node to document */
PHP_FUNCTION(domxml_doc_add_root)
{
	zval *id, *rv = NULL;
	xmlDoc *docp;
	xmlNode *nodep, *root;
	int ret, name_len;
	char *name;

	DOMXML_PARAM_TWO(docp, id, le_domxmldocp, "s", &name, &name_len);

	nodep = xmlNewDocNode(docp, NULL, name, NULL);
	if (!nodep) {
		RETURN_FALSE;
	}

	if ((root = xmlDocSetRootElement(docp, nodep)) != NULL) {
		/* Root node already unlinked from xmlDocSetRootElement */
		if (dom_object_get_data(root) == NULL) {
			node_list_unlink(root->children);
			node_list_unlink((xmlNodePtr) root->properties);
			xmlFreeNode(root);
		}
	}

	DOMXML_RET_OBJ(rv, nodep, &ret);
}
/* }}} */

/* {{{ proto bool domxml_set_root(int domnode)
   Sets root node of document */
PHP_FUNCTION(domxml_doc_set_root)
{
	zval *id, *rv, *node;
	xmlDoc *docp;
	xmlNode *root;

	DOMXML_PARAM_TWO(docp, id, le_domxmldocp, "o", &node, &rv);
	DOMXML_GET_OBJ(root, node, le_domxmlnodep);

	if (!root) {
		RETURN_FALSE;
	}

	xmlDocSetRootElement(docp, root);

	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_doc_validate(array &error)
   Validates a DomDocument according to his DTD*/
PHP_FUNCTION(domxml_doc_validate)
{
	zval *id;
	xmlValidCtxt cvp;
	xmlDoc *docp;
	domxml_ErrorCtxt errorCtxt;
	zval *errors ;
	    
	DOMXML_PARAM_ONE(docp, id, le_domxmldocp,"|z",&errors);
	errorCtxt.valid = &cvp;

	if (ZEND_NUM_ARGS() == 1) {
		zval_dtor(errors);
		array_init(errors);
		errorCtxt.errors = errors;
	} else {
		errorCtxt.errors = NULL;
	}

	errorCtxt.parser = NULL;
	cvp.userData = (void *) &errorCtxt;
	cvp.error    = (xmlValidityErrorFunc) domxml_error_validate;
	cvp.warning  = (xmlValidityWarningFunc) domxml_error_validate;
	if (docp->intSubset == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "No DTD given in XML-Document");
	}
    
	if (xmlValidateDocument(&cvp, docp)) {
		RETVAL_TRUE;
	} else {
		RETVAL_FALSE;
	}
}
/* }}} */

/* {{{ proto object domxml_new_xmldoc(string version)
   Creates new xmldoc */
PHP_FUNCTION(domxml_new_xmldoc)
{
	zval *rv = NULL;
	xmlDoc *docp;
	int ret, buf_len;
	char *buf;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
		return;
	}

	docp = xmlNewDoc(buf);
	if (!docp) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, (xmlNodePtr) docp, &ret);
}
/* }}} */

/* {{{ proto bool domxml_doc_free_doc()
   Frees xmldoc and removed objects from hash */
PHP_FUNCTION(domxml_doc_free_doc)
{
	zval *doc;
	xmlNode *docp;

	DOMXML_GET_THIS_OBJ(docp, doc, le_domxmldocp);

	if (! (docp->type == XML_DOCUMENT_NODE || docp->type == XML_HTML_DOCUMENT_NODE) ) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "DomDocument is required");
		RETURN_FALSE;
	}

	/* No need to do this as php_free_xml_doc will kill the children
	node_list_wrapper_dtor(docp->children, 1 TSRMLS_CC);
	node_list_wrapper_dtor((xmlNodePtr) docp->properties, 1 TSRMLS_CC);
	*/
	node_wrapper_free(docp TSRMLS_CC);

	RETURN_TRUE;
}
/* }}} */

/* {{{ proto object domxml_parser([string buf[,string filename]])
   Creates new xmlparser */
PHP_FUNCTION(domxml_parser)
{
	zval *rv;
	xmlParserCtxtPtr parserp;
	int ret, buf_len = 0;
	char *buf = "";
	char *filename = NULL;
	int filename_len = 0;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ss", &buf, &buf_len, &filename, &filename_len) == FAILURE) {
		return;
	}

	parserp =  xmlCreatePushParserCtxt(NULL, NULL, buf, buf_len, filename);
	if (!parserp) {
		RETURN_FALSE;
	}
/*	parserp->loadsubset = XML_DETECT_IDS; */

	rv = php_xmlparser_new(parserp, &ret TSRMLS_CC);
	DOMXML_RET_ZVAL(rv);
}
/* }}} */

/* {{{ proto bool domxml_parser_start_document()
   starts a document*/
PHP_FUNCTION(domxml_parser_start_document)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	
	DOMXML_PARAM_NONE(parserp, id, le_domxmlparserp);
	startDocument(parserp);
	
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_parser_end_document()
   ends a document */
PHP_FUNCTION(domxml_parser_end_document)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	
	DOMXML_PARAM_NONE(parserp, id, le_domxmlparserp);
	endDocument(parserp);
	
}
/* }}} */

/* {{{ proto bool domxml_parser_start_element(string tagname, array attributes)
   Starts an element and adds attributes*/
PHP_FUNCTION(domxml_parser_start_element)
{
	zval *id,*params = NULL;
	xmlParserCtxtPtr parserp;
	char *tagname;
	int tagname_len;
	char **atts = NULL;

	DOMXML_PARAM_THREE(parserp, id, le_domxmlparserp,"s|a", &tagname, &tagname_len, &params);
	if (params != NULL) {
		atts = php_xmlparser_make_params(params TSRMLS_CC);
	}
	if (parserp->myDoc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document was not started");
		RETURN_FALSE;
	}
	startElement(parserp, (xmlChar *) tagname,  (const xmlChar **) atts);
	
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_parser_end_element(string tagname)
   Ends an element */
PHP_FUNCTION(domxml_parser_end_element)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	char *tagname;
	int tagname_len;
	
	DOMXML_PARAM_TWO(parserp, id, le_domxmlparserp,"s", &tagname, &tagname_len);

	if (parserp->myDoc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document was not started");
		RETURN_FALSE;
	}

	endElement(parserp, (xmlChar *) tagname);
	
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_parser_comment(string comment)
   Adds a comment */
PHP_FUNCTION(domxml_parser_comment)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	char *commentstring;
	int commentstring_len;
	
	DOMXML_PARAM_TWO(parserp, id, le_domxmlparserp,"s", &commentstring, &commentstring_len);

	if (parserp->myDoc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document was not started");
		RETURN_FALSE;
	}

	comment(parserp, (xmlChar *) commentstring);
	
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_parser_cdata_section(string chunk)
   adds a cdata block */
PHP_FUNCTION(domxml_parser_cdata_section)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	char *chunk;
	int chunk_len;
	
	DOMXML_PARAM_TWO(parserp, id, le_domxmlparserp,"s", &chunk, &chunk_len);

	if (parserp->myDoc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document was not started");
		RETURN_FALSE;
	}

	cdataBlock(parserp, (xmlChar *) chunk, chunk_len);
	
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_parser_characters(string characters)
   Adds characters */
PHP_FUNCTION(domxml_parser_characters)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	char *charactersstring;
	int characters_len;
	
	DOMXML_PARAM_TWO(parserp, id, le_domxmlparserp,"s", &charactersstring, &characters_len);

	if (parserp->myDoc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document was not started");
		RETURN_FALSE;
	}

	characters(parserp, (xmlChar *) charactersstring, characters_len);
	
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_parser_entity_reference(string reference)
   Adds entity reference */
PHP_FUNCTION(domxml_parser_entity_reference)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	char *referencestring;
	int reference_len;
	
	DOMXML_PARAM_TWO(parserp, id, le_domxmlparserp,"s", &referencestring, &reference_len);

	if (parserp->myDoc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document was not started");
		RETURN_FALSE;
	}

	reference(parserp, (xmlChar *) referencestring);
	
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_parser_processing_instruction(string target, string data)
   Adds processing instruction */
PHP_FUNCTION(domxml_parser_processing_instruction)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	char *data,*target;
	int data_len, target_len;
	
	DOMXML_PARAM_FOUR(parserp, id, le_domxmlparserp,"ss", &target, &target_len, &data, &data_len);

	if (parserp->myDoc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document was not started");
		RETURN_FALSE;
	}

	processingInstruction(parserp, (xmlChar *) target, (xmlChar *) data);
	
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_parser_namespace_decl(string href, string prefix)
   Adds namespace declaration */
PHP_FUNCTION(domxml_parser_namespace_decl)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	char *href,*prefix;
	int href_len, prefix_len;
	
	DOMXML_PARAM_FOUR(parserp, id, le_domxmlparserp,"ss", &href, &href_len, &prefix, &prefix_len);

	if (parserp->myDoc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Document was not started");
		RETURN_FALSE;
	}

	namespaceDecl(parserp, (xmlChar *) href, (xmlChar *) prefix);
	
	RETURN_TRUE;
}
/* }}} */

/* {{{ proto bool domxml_parser_add_chunk(string chunk)
   adds xml-chunk to parser */
PHP_FUNCTION(domxml_parser_add_chunk)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	char *chunk;
	int chunk_len, error;

	DOMXML_PARAM_TWO(parserp, id, le_domxmlparserp,"s", &chunk, &chunk_len);
	error = xmlParseChunk(parserp, chunk, chunk_len , 0);
	if (error != 0) {
		RETURN_FALSE;
	}

	RETURN_TRUE;
}
/* }}} */

/* {{{ proto object domxml_parser_end([string chunk])
   Ends parsing and returns DomDocument*/
PHP_FUNCTION(domxml_parser_end)
{
	zval *id,*rv = NULL;
	xmlParserCtxtPtr parserp;
	char *chunk = NULL;
	int chunk_len = 0, error;
	int ret;


	DOMXML_PARAM_TWO(parserp, id, le_domxmlparserp,"|s", &chunk, &chunk_len);
	error = xmlParseChunk(parserp, chunk, chunk_len, 1);
	if (error != 0) {
		php_error_docref(NULL TSRMLS_CC, E_ERROR, "Error: %d", error);
		RETURN_FALSE;
	}
	if (parserp->myDoc != NULL) {
		DOMXML_RET_OBJ(rv, (xmlNodePtr) parserp->myDoc, &ret);
	}
	else {
		RETVAL_FALSE
	}
}
/* }}} */

/* {{{ proto object domxml_parser_get_document()
   Returns DomDocument from parser */
PHP_FUNCTION(domxml_parser_get_document)
{
	zval *id,*rv = NULL;
	xmlParserCtxtPtr parserp;
	int ret;

	DOMXML_PARAM_NONE(parserp, id, le_domxmlparserp);

	if (parserp->myDoc != NULL) {
		DOMXML_RET_OBJ(rv, (xmlNodePtr) parserp->myDoc, &ret);
	}
	else {
		RETVAL_FALSE
	}
}
/* }}} */

/* {{{ proto bool domxml_parser_set_keep_blanks(bool mode)
   Determines how to handle blanks */
PHP_FUNCTION(domxml_parser_set_keep_blanks)
{
	zval *id;
	xmlParserCtxtPtr parserp;
	zend_bool mode;

	DOMXML_PARAM_ONE(parserp, id, le_domxmlparserp, "b", &mode);
	parserp->keepBlanks = mode;

	RETURN_TRUE;
}
/* }}} */

#ifdef newcode
/* {{{ proto int node_namespace([int node])
   Returns list of namespaces */
static int node_namespace(zval **attributes, xmlNode *nodep TSRMLS_DC)
{
	xmlNs *ns;

	/* Get the children of the current node */
	ns = nodep->ns;
	if (!ns) {
		return -1;
	}

	/* create an php array for the children */
	MAKE_STD_ZVAL(*attributes);
	if (array_init(*attributes) == FAILURE) {
		return -1;
	}

	while (ns) {
		zval *pattr;
		int ret;

		pattr = php_domobject_new((xmlNodePtr) ns, &ret, NULL TSRMLS_CC);
		SEPARATE_ZVAL(&pattr);

/*		if(!ret) { */
		if (ns->href)
			add_property_stringl(pattr, "href", (char *) ns->href, strlen(ns->href), 1);
		if (ns->prefix)
			add_property_stringl(pattr, "prefix", (char *) ns->prefix, strlen(ns->prefix), 1);
		add_property_long(pattr, "type", Z_TYPE_P(ns));
/*		} */

		zend_hash_next_index_insert(Z_ARRVAL_PP(attributes), &pattr, sizeof(zval *), NULL);
		ns = ns->next;
	}
	return 0;
}
/* }}} */
#endif

/* {{{ int node_attributes(zval **attributes, int node)
   Returns list of children nodes */
static int node_attributes(zval **attributes, xmlNode *nodep TSRMLS_DC)
{
	xmlAttr *attr;
	int count = 0;

	/* Get the children of the current node */
	if (Z_TYPE_P(nodep) != XML_ELEMENT_NODE)
		return -1;
	attr = nodep->properties;
	if (!attr)
		return -2;

	/* create an php array for the children */
	MAKE_STD_ZVAL(*attributes);
	array_init(*attributes);

	while (attr) {
		zval *pattr;
		int ret;
		xmlChar *content;

		pattr = php_domobject_new((xmlNodePtr) attr, &ret, NULL TSRMLS_CC);
		/** XXX FIXME XXX */
/*		if(0 <= (n = node_children(&children, attr->children TSRMLS_CC))) {
			zend_hash_update(Z_OBJPROP_P(value), "children", sizeof("children"), (void *) &children, sizeof(zval *), NULL);
		}
*/		
        add_property_string(pattr, "name", (char *) (attr->name), 1);
		content = xmlNodeGetContent((xmlNodePtr) attr);
		add_property_string(pattr, "value", content, 1);
		xmlFree(content);
		zend_hash_next_index_insert(Z_ARRVAL_PP(attributes), &pattr, sizeof(zval *), NULL);
		attr = attr->next;
		count++;
	}
	return count;
}
/* }}} */

/* {{{ proto int node_children([int node])
   Returns list of children nodes */
static int node_children(zval **children, xmlNode *nodep TSRMLS_DC)
{
	zval *mchildren, *attributes;
	/* zval *namespace; */
	xmlNode *last;
	int count = 0;

	/* Get the children of the current node */
	last = nodep;
	if (!last) {
		return -1;
	}

	/* create an php array for the children */
	MAKE_STD_ZVAL(*children);
	array_init(*children);

	while (last) {
		zval *child;
		int ret;

		if (NULL != (child = php_domobject_new(last, &ret, NULL TSRMLS_CC))) {
			zend_hash_next_index_insert(Z_ARRVAL_PP(children), &child, sizeof(zval *), NULL);

			/* Get the namespace of the current node and add it as a property */
			/* XXX FIXME XXX */
/*
			if(!node_namespace(&namespace, last))
				zend_hash_update(Z_OBJPROP_P(child), "namespace", sizeof("namespace"), (void *) &namespace, sizeof(zval *), NULL);
*/

			/* Get the attributes of the current node and add it as a property */
			if (node_attributes(&attributes, last TSRMLS_CC) >= 0)
				zend_hash_update(Z_OBJPROP_P(child), "attributes", sizeof("attributes"), (void *) &attributes, sizeof(zval *), NULL);

			/* Get recursively the children of the current node and add it as a property */
			if (node_children(&mchildren, last->children TSRMLS_CC) >= 0)
				zend_hash_update(Z_OBJPROP_P(child), "children", sizeof("children"), (void *) &mchildren, sizeof(zval *), NULL);

			count++;
		}
		last = last->next;
	}
	return count;
}
/* }}} */

/* {{{ proto object domxml_xmltree(string xmltree)
   Creates a tree of PHP objects from an XML document */
PHP_FUNCTION(domxml_xmltree)
{
	zval *children, *rv = NULL;
	xmlDoc *docp;
	xmlNode *root;
	int ret, buf_len;
	char *buf;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
		return;
	}

	/* Create a new xml document */
	docp = domxml_document_parser(0, 0, buf, NULL TSRMLS_CC);

	if (!docp) {
		RETURN_FALSE;
	}

	/* get the root and add as a property to the document */
	root = docp->children;
	if (!root) {
		xmlFreeDoc(docp);
		RETURN_FALSE;
	}

	DOMXML_DOMOBJ_NEW(rv, (xmlNodePtr) docp, &ret);

	/* The root itself maybe an array. Though you may not have two Elements
	   as root, you may have a comment, pi and and element as root.
	   Thanks to Paul DuBois for pointing me at this.
	 */
	if (node_children(&children, root TSRMLS_CC) >= 0) {
		zend_hash_update(Z_OBJPROP_P(rv), "children",sizeof("children"), (void *) &children, sizeof(zval *), NULL);
	}

	DOMXML_RET_ZVAL(rv);
/*	xmlFreeDoc(docp); */
}
/* }}} */

#if defined(LIBXML_XPATH_ENABLED)
/* {{{ proto bool xpath_init(void)
   Initializing XPath environment */
PHP_FUNCTION(xpath_init)
{
	if (ZEND_NUM_ARGS() != 0) {
		WRONG_PARAM_COUNT;
	}

	xmlXPathInit();
	RETURN_TRUE;
}
/* }}} */

/* {{{ php_xpathptr_new_context()
 */
static void php_xpathptr_new_context(INTERNAL_FUNCTION_PARAMETERS, int mode)
{
	zval *id, *rv;
	xmlXPathContextPtr ctx;
	xmlDocPtr docp;
	int ret;

	DOMXML_PARAM_NONE(docp, id, le_domxmldocp);

#if defined(LIBXML_XPTR_ENABLED)
	if (mode == PHP_XPTR)
		ctx = xmlXPtrNewContext(docp, NULL, NULL);
	else
#endif
		ctx = xmlXPathNewContext(docp);
	if (!ctx) {
		RETURN_FALSE;
	}

	rv = php_xpathcontext_new(ctx, &ret TSRMLS_CC);
	DOMXML_RET_ZVAL(rv);
}
/* }}} */

/* {{{ proto object xpath_new_context([int doc_handle])
   Creates new XPath context */
PHP_FUNCTION(xpath_new_context)
{
	php_xpathptr_new_context(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_XPATH);
}
/* }}} */

/* {{{ proto object xptr_new_context([int doc_handle])
   Creates new XPath context */
PHP_FUNCTION(xptr_new_context)
{
	php_xpathptr_new_context(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_XPTR);
}
/* }}} */

/* {{{ php_xpathptr_eval()
 */
static void php_xpathptr_eval(INTERNAL_FUNCTION_PARAMETERS, int mode, int expr)
{
	zval *id, *rv, *contextnode = NULL;
	xmlXPathContextPtr ctxp;
	xmlXPathObjectPtr xpathobjp;
	xmlNode *contextnodep;
	int ret, str_len;
	char *str;

	contextnode = NULL;
	contextnodep = NULL;

	if (NULL == (id = getThis())) {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "os|o", &id, &str, &str_len, &contextnode) == FAILURE) {
			return;
		}
	} else {
		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|o", &str, &str_len, &contextnode) == FAILURE) {
			return;
		}
	}

	ctxp = php_xpath_get_context(id, le_xpathctxp, 0 TSRMLS_CC);
	if (!ctxp) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot fetch XPATH context");
		RETURN_FALSE;
	}

	if (contextnode) {
		DOMXML_GET_OBJ(contextnodep, contextnode, le_domxmlnodep);
	} else {
		contextnodep = xmlDocGetRootElement(ctxp->doc);
	}
	ctxp->node = contextnodep;

#if defined(LIBXML_XPTR_ENABLED)
	if (mode == PHP_XPTR) {
		xpathobjp = xmlXPtrEval(BAD_CAST str, ctxp);
	} else {
#endif
		if (expr) {
			xpathobjp = xmlXPathEvalExpression(str, ctxp);
		} else {
			xpathobjp = xmlXPathEval(str, ctxp);
		}
#if defined(LIBXML_XPTR_ENABLED)
	}
#endif

	ctxp->node = NULL;
	if (!xpathobjp) {
		RETURN_FALSE;
	}

	if (NULL == (rv = php_xpathobject_new(xpathobjp, &ret TSRMLS_CC))) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create required XPATH objcet");
		RETURN_FALSE;
	}
	SEPARATE_ZVAL(&rv);

	add_property_long(rv, "type", Z_TYPE_P(xpathobjp));

	switch (Z_TYPE_P(xpathobjp)) {

		case XPATH_UNDEFINED:
			break;

		case XPATH_NODESET:
		{
			int i;
			zval *arr;
			xmlNodeSetPtr nodesetp;

			MAKE_STD_ZVAL(arr);
			if (array_init(arr) == FAILURE) {
				zval_dtor(rv);
				RETURN_FALSE;
			}

			if (NULL == (nodesetp = xpathobjp->nodesetval)) {
				zval_dtor(rv);
				RETURN_FALSE;
			}

			for (i = 0; i < nodesetp->nodeNr; i++) {
				xmlNodePtr node = nodesetp->nodeTab[i];
				zval *child;
				int retnode;
				xmlNsPtr curns;
				xmlNodePtr nsparent;
				
				if (node->type == XML_NAMESPACE_DECL) {
					nsparent = node->_private;
					curns = xmlNewNs(NULL, node->name, NULL);
					if (node->children) {
						curns->prefix = xmlStrdup((char *) node->children);
					}
					if (node->children) {
						node = xmlNewDocNode(ctxp->doc, NULL, (char *) node->children, node->name);
					} else {
						node = xmlNewDocNode(ctxp->doc, NULL, "xmlns", node->name);
					}
					node->type = XML_NAMESPACE_DECL;
					node->parent = nsparent;
					node->ns = curns;
				}

				child = php_domobject_new(node, &retnode, NULL TSRMLS_CC);

				zend_hash_next_index_insert(Z_ARRVAL_P(arr), &child, sizeof(zval *), NULL);
			}
			zend_hash_update(Z_OBJPROP_P(rv), "nodeset", sizeof("nodeset"), (void *) &arr, sizeof(zval *), NULL);
			break;
		}

		case XPATH_BOOLEAN:
			add_property_bool(rv, "value", xpathobjp->boolval);
			break;

		case XPATH_NUMBER:
			add_property_double(rv, "value", xpathobjp->floatval);
			break;

		case XPATH_STRING:
			add_property_string(rv, "value", xpathobjp->stringval, 1);
			break;

		case XPATH_POINT:
			break;

		case XPATH_RANGE:
			break;

		case XPATH_LOCATIONSET:
			break;

		case XPATH_USERS:
			break;

		case XPATH_XSLT_TREE:
			break;
	}

	xmlXPathFreeObject(xpathobjp);
	*return_value = *rv;
	FREE_ZVAL(rv);
}
/* }}} */

/* {{{ proto object xpath_eval([object xpathctx_handle,] string str)
   Evaluates the XPath Location Path in the given string */
PHP_FUNCTION(xpath_eval)
{
	php_xpathptr_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_XPATH, 0);
}
/* }}} */

/* {{{ proto object xpath_eval_expression([object xpathctx_handle,] string str)
   Evaluates the XPath expression in the given string */
PHP_FUNCTION(xpath_eval_expression)
{
	php_xpathptr_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_XPATH, 1);
}
/* }}} */

/* {{{ proto bool xpath_register_ns([object xpathctx_handle,] string namespace_prefix, string namespace_uri)
	Registeres the given namespace in the passed XPath context */
PHP_FUNCTION(xpath_register_ns)
{
	/*
	TODO:
	- automagically register all namespaces when creating a new context
	*/

	int prefix_len, uri_len, result;
	xmlXPathContextPtr ctxp;
	char *prefix, *uri;
	zval *id;

	DOMXML_PARAM_FOUR(ctxp, id, le_xpathctxp, "ss", &prefix, &prefix_len, &uri, &uri_len);

	ctxp->node = NULL;

	#ifdef CHREGU_0
	/* this leads to memleaks... commenting it out, as it works for me without copying
	   it. chregu */
	/*
		this is a hack - libxml2 doesn't copy the URI, it simply uses the string
		given in the parameter - which is normally deallocated after the function
	*/
	uri_static = estrndup(uri, uri_len);
	result = xmlXPathRegisterNs(ctxp, prefix, uri_static);
	#endif
	
	result = xmlXPathRegisterNs(ctxp, prefix, uri);

	if (0 == result) {
		RETURN_TRUE;
	}
	RETURN_FALSE;
}
/* }}} */
#endif	/* defined(LIBXML_XPATH_ENABLED) */

#if defined(LIBXML_XPTR_ENABLED)
/* {{{ proto int xptr_eval([int xpathctx_handle,] string str)
   Evaluates the XPtr Location Path in the given string */
PHP_FUNCTION(xptr_eval)
{
	php_xpathptr_eval(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_XPTR, 0);
}
/* }}} */
#endif	/* LIBXML_XPTR_ENABLED */

/* {{{ proto string domxml_version(void)
   Get XML library version */
PHP_FUNCTION(domxml_version)
{
	RETURN_STRING((char *) xmlParserVersion,1);
}
/* }}} */

/* {{{ proto int domxml_doc_xinclude()
   Substitutues xincludes in a DomDocument */
PHP_FUNCTION(domxml_doc_xinclude)
{
	zval *id;
	xmlDoc *docp;
	int err; 
	
	DOMXML_PARAM_NONE(docp, id, le_domxmldocp);
	
	err = xmlXIncludeProcess (docp);
	
	if (err) {
		RETVAL_LONG(err);
	} else {
		RETVAL_FALSE;
	}
    
}
/* }}} */

#if HAVE_DOMXSLT
static zval *php_xsltstylesheet_new(xsltStylesheetPtr obj, int *found TSRMLS_DC)
{
	zval *wrapper;
	int rsrc_type;

	*found = 0;

	if (!obj) {
		MAKE_STD_ZVAL(wrapper);
		ZVAL_NULL(wrapper);
		return wrapper;
	}

	if ((wrapper = (zval *) dom_object_get_data((void *) obj))) {
		zval_add_ref(&wrapper);
		*found = 1;
		return wrapper;
	}

	MAKE_STD_ZVAL(wrapper);

	object_init_ex(wrapper, domxsltstylesheet_class_entry);
	rsrc_type = le_domxsltstylesheetp;
	php_xsltstylesheet_set_object(wrapper, (void *) obj, rsrc_type TSRMLS_CC);

	return (wrapper);
}

/* {{{ proto object domxml_xslt_stylesheet(string xsltstylesheet)
   Creates XSLT Stylesheet object from string */
PHP_FUNCTION(domxml_xslt_stylesheet)
{
	zval *rv;
	xmlDocPtr docp;
	xsltStylesheetPtr sheetp;
	int ret, mode, prevSubstValue, prevExtDtdValue;
	char *buffer;
	int buffer_len;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buffer, &buffer_len) == FAILURE) {
		RETURN_FALSE;
	}

	mode = DOMXML_LOAD_SUBSTITUTE_ENTITIES | DOMXML_LOAD_COMPLETE_ATTRS;
	docp = domxml_document_parser(mode, 0, buffer, NULL TSRMLS_CC);

	if (!docp)
		RETURN_FALSE;

	prevSubstValue = xmlSubstituteEntitiesDefault(1);
	prevExtDtdValue = xmlLoadExtDtdDefaultValue;
	xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
	sheetp = xsltParseStylesheetDoc(docp);
	xmlSubstituteEntitiesDefault(prevSubstValue);
	xmlLoadExtDtdDefaultValue = prevExtDtdValue;

	if (!sheetp) {
		xmlFreeDoc(docp);
		RETURN_FALSE;
	}

	rv = php_xsltstylesheet_new(sheetp, &ret TSRMLS_CC);
	DOMXML_RET_ZVAL(rv);
}
/* }}} */

/* {{{ proto object domxml_xslt_stylesheet_doc(object xmldoc)
   Creates XSLT Stylesheet object from DOM Document object */
PHP_FUNCTION(domxml_xslt_stylesheet_doc)
{
	zval *rv, *idxml;
	xmlDocPtr docp;
	xmlDocPtr newdocp;
	xsltStylesheetPtr sheetp;
	int ret;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &idxml) == FAILURE) {
		RETURN_FALSE;
	}

	DOMXML_GET_OBJ(docp, idxml, le_domxmldocp);

	newdocp = xmlCopyDoc(docp, 1);

	if (!newdocp)
		RETURN_FALSE;

	sheetp = xsltParseStylesheetDoc(newdocp);

	if (!sheetp) {
		xmlFreeDoc(newdocp);
		RETURN_FALSE;
	}
	rv = php_xsltstylesheet_new(sheetp, &ret TSRMLS_CC);
	DOMXML_RET_ZVAL(rv);
}
/* }}} */

/* {{{ proto object domxml_xslt_stylesheet_file(string filename)
   Creates XSLT Stylesheet object from file */
PHP_FUNCTION(domxml_xslt_stylesheet_file)
{
	zval *rv;
	xmlDocPtr docp;
	xsltStylesheetPtr sheetp;
	int ret, file_len, mode, prevSubstValue, prevExtDtdValue;
	char *file;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) {
		RETURN_FALSE;
	}

	mode = DOMXML_LOAD_SUBSTITUTE_ENTITIES | DOMXML_LOAD_COMPLETE_ATTRS;
	docp = domxml_document_parser(mode, 1, file, NULL TSRMLS_CC);

	if (!docp)
		RETURN_FALSE;

	prevSubstValue = xmlSubstituteEntitiesDefault(1);
	prevExtDtdValue = xmlLoadExtDtdDefaultValue;
	xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
	sheetp = xsltParseStylesheetDoc(docp);
	xmlSubstituteEntitiesDefault(prevSubstValue);
	xmlLoadExtDtdDefaultValue = prevExtDtdValue;

	if (!sheetp) {
		xmlFreeDoc(docp);
		RETURN_FALSE;
	}

	rv = php_xsltstylesheet_new(sheetp, &ret TSRMLS_CC);
	DOMXML_RET_ZVAL(rv);
}
/* }}} */

/* {{{ php_domxslt_string_to_xpathexpr()
   Translates a string to a XPath Expression */
static char *php_domxslt_string_to_xpathexpr(const char *str TSRMLS_DC)
{
	const xmlChar *string = (const xmlChar *)str;

	xmlChar *value;
	int str_len;
	
	str_len = xmlStrlen(string) + 3;
	
	if (xmlStrchr(string, '"')) {
		if (xmlStrchr(string, '\'')) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create XPath expression (string contains both quote and double-quotes)");
			return NULL;
		}
		value = (xmlChar*) emalloc (str_len * sizeof(xmlChar) );
		snprintf(value, str_len, "'%s'", string);
	} else {
		value = (xmlChar*) emalloc (str_len * sizeof(xmlChar) );
		snprintf(value, str_len, "\"%s\"", string);
	}

	return (char *)value;
}

/* {{{ php_domxslt_make_params()
   Translates a PHP array to a libxslt parameters array */
static char **php_domxslt_make_params(zval *idvars, int xpath_params TSRMLS_DC)
{
	HashTable *parht;
	int parsize;
	zval **value;
	char *xpath_expr, *string_key = NULL;
	ulong num_key;
	char **params = NULL;
	int i = 0;

	parht = HASH_OF(idvars);
	parsize = (2 * zend_hash_num_elements(parht) + 1) * sizeof(char *);
	params = (char **)emalloc(parsize);
	memset((char *)params, 0, parsize);

	for (zend_hash_internal_pointer_reset(parht);
		zend_hash_get_current_data(parht, (void **)&value) == SUCCESS;
		zend_hash_move_forward(parht)) {

		if (zend_hash_get_current_key(parht, &string_key, &num_key, 1) != HASH_KEY_IS_STRING) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid argument or parameter array");
			return NULL;
		}
		else {
			SEPARATE_ZVAL(value);
			convert_to_string_ex(value);

			if (!xpath_params) {
				xpath_expr = php_domxslt_string_to_xpathexpr(Z_STRVAL_PP(value) TSRMLS_CC);
			}
			else {
				xpath_expr = Z_STRVAL_PP(value);
			}

			if (xpath_expr) {
				params[i++] = string_key;
				params[i++] = xpath_expr;
			}
		}
	}

	params[i++] = NULL;

	return params;
}
/* }}} */

static int xslt_has_xsl_keys (xmlDocPtr doc) {
	
	xmlNode *nodep;
	nodep = xmlDocGetRootElement(doc)->children;
	while (nodep) {
		if (nodep->type == XML_ELEMENT_NODE && xmlStrEqual(nodep->name, "key") && xmlStrEqual(nodep->ns->href, XSLT_NAMESPACE)) {
				return 1;
				break;
			}
		nodep = nodep->next;
	}
	return 0;
}


/* {{{ proto object domxml_xslt_process(object xslstylesheet, object xmldoc [, array xslt_parameters [, bool xpath_parameters [, string profileFilename]]])
   Perform an XSLT transformation */
PHP_FUNCTION(domxml_xslt_process)
{
/* TODO:
	- test memory deallocation
	- test other stuff
	- check xsltsp->errors ???
*/
	zval *rv = NULL, *idxsl, *idxml, *idparams = NULL;
	zend_bool xpath_params = 0;
	xsltStylesheetPtr xsltstp;
	xmlDocPtr xmldocp;
	xmlDocPtr docp;
	char **params = NULL;
	int ret;
	long clone = -1;
	char *filename;
	int filename_len = 0;
	FILE *f;

	DOMXML_GET_THIS(idxsl);

	xsltstp = php_xsltstylesheet_get_object(idxsl, le_domxsltstylesheetp, 0 TSRMLS_CC);
	if (!xsltstp) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing");
		RETURN_FALSE;
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|a!b!s!l", &idxml, &idparams, &xpath_params, &filename, &filename_len, &clone) == FAILURE) {
		RETURN_FALSE;
	}

	if (filename_len && !(f = php_stream_open_wrapper_as_file(filename, "w", ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL))) {
		RETURN_FALSE;
	}

	DOMXML_GET_OBJ(xmldocp, idxml, le_domxmldocp);
	if (clone == -1 && xslt_has_xsl_keys(xsltstp->doc) == 1) {
		clone = 1;
	}
	
	if (idparams) {
		params = php_domxslt_make_params(idparams, xpath_params TSRMLS_CC);
	}

	if (clone == 1) {
		xmldocp = xmlCopyDoc(xmldocp, 1);
	}

	if (filename_len) {
		docp = xsltProfileStylesheet(xsltstp, xmldocp, (const char**)params, f);
		fclose(f);
	} else {
		docp = xsltApplyStylesheet(xsltstp, xmldocp, (const char**)params);
	}

	if (clone == 1) {
		xmlFreeDoc(xmldocp);
	}

	if (params) {
		efree(params);
	}

	if (!docp) {
		RETURN_FALSE;
	}

	DOMXML_RET_OBJ(rv, (xmlNodePtr) docp, &ret);
}
/* }}} */

/* {{{ proto string domxml_xslt_result_dump_mem(object xslstylesheet, object xmldoc)
   output XSLT result to memory */
PHP_FUNCTION(domxml_xslt_result_dump_mem)
{
	zval *idxsl, *idxml;
	xsltStylesheetPtr xsltstp;
	xmlDocPtr xmldocp;
	xmlChar *doc_txt_ptr;
	int doc_txt_len;
	int ret;

	DOMXML_GET_THIS(idxsl);

	xsltstp = php_xsltstylesheet_get_object(idxsl, le_domxsltstylesheetp, 0 TSRMLS_CC);
	if (!xsltstp) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing");
		RETURN_FALSE;
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &idxml) == FAILURE) {
		RETURN_FALSE;
	}

	DOMXML_GET_OBJ(xmldocp, idxml, le_domxmldocp);

	ret = xsltSaveResultToString(&doc_txt_ptr, &doc_txt_len, xmldocp, xsltstp);
	if (ret < 0) {
		RETURN_FALSE;
	}
	
	if (doc_txt_ptr) {
		RETVAL_STRINGL(doc_txt_ptr, doc_txt_len, 1);
		xmlFree(doc_txt_ptr);
	} else {
		RETURN_EMPTY_STRING();
	}
}
/* }}} */

/* {{{ proto int domxml_xslt_result_dump_file(object xslstylesheet, object xmldoc, string filename[, int compression])
   output XSLT result to File */
PHP_FUNCTION(domxml_xslt_result_dump_file)
{
	zval *idxsl, *idxml;
	xsltStylesheetPtr xsltstp;
	xmlDocPtr xmldocp;
	char *filename;
	int filename_len;
	int ret;
	long compression = 0;

	DOMXML_GET_THIS(idxsl);

	xsltstp = php_xsltstylesheet_get_object(idxsl, le_domxsltstylesheetp, 0 TSRMLS_CC);
	if (!xsltstp) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Underlying object missing");
		RETURN_FALSE;
	}

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "os|l", &idxml, &filename, &filename_len, &compression) == FAILURE) {
		RETURN_FALSE;
	}
	if ((PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) || php_check_open_basedir(filename TSRMLS_CC)) {
		RETURN_FALSE;
	}

	DOMXML_GET_OBJ(xmldocp, idxml, le_domxmldocp);

	ret = xsltSaveResultToFilename(filename, xmldocp, xsltstp, compression);

	if (ret < 0) {
		RETURN_FALSE;
	}

	RETURN_LONG(ret);
}
/* }}} */

/* {{{ proto string domxml_xslt_version(void)
   Get XSLT library version */
PHP_FUNCTION(domxml_xslt_version)
{
	RETURN_LONG(xsltLibxsltVersion);
}
/* }}} */
#endif /* HAVE_DOMXSLT */

#ifdef PHP_WIN32
__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
}
#endif

#endif /* HAVE_DOMXML */

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: noet sw=4 ts=4 fdm=marker
 * vim<600: noet sw=4 ts=4
 */
