From: Christian Stocker Date: Tue, 8 Apr 2003 18:26:50 +0000 (+0000) Subject: Big Patch with a lot of mem-leak fixes X-Git-Tag: php-4.3.2RC2~135 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d7f0682135a79d631e6505b02c738e126555bdb5;p=php Big Patch with a lot of mem-leak fixes - Attribute nodes are freed/generated correctly - xsltstylesheets are freed correctly - xpathregister doesn't leak anymore - parser/xsltglobals cleanup at MSHUTDOWN - Added DomDocument->free() for removing documents during script-time --- diff --git a/ext/domxml/php_domxml.c b/ext/domxml/php_domxml.c index 5df30520d0..4db699dbc6 100644 --- a/ext/domxml/php_domxml.c +++ b/ext/domxml/php_domxml.c @@ -316,6 +316,7 @@ static function_entry php_domxmldoc_class_functions[] = { 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) */ @@ -529,8 +530,8 @@ zend_module_entry domxml_module_entry = { "domxml", domxml_functions, PHP_MINIT(domxml), + PHP_MSHUTDOWN(domxml), NULL, - PHP_RINIT(domxml), NULL, PHP_MINFO(domxml), DOMXML_API_VERSION, /* Extension versionnumber */ @@ -589,29 +590,104 @@ static inline void node_wrapper_dtor(xmlNodePtr node) } +/* This function should only be used when freeing nodes + as dependant objects are destroyed */ +static inline void node_wrapper_free(xmlNodePtr node) +{ + 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) { + TSRMLS_FETCH(); + 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; } } - -static inline void node_list_wrapper_dtor(xmlNodePtr node) +/* 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) { while (node != NULL) { - node_list_wrapper_dtor(node->children); + node_list_wrapper_dtor(node->children, destroyref); switch (node->type) { /* Skip property freeing for the following types */ case XML_ATTRIBUTE_DECL: case XML_DTD_NODE: case XML_ENTITY_DECL: + case XML_ATTRIBUTE_NODE: break; default: - attr_list_wrapper_dtor(node->properties); + /* 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); } - node_wrapper_dtor(node); + + if (destroyref == 1) { + node_wrapper_free(node); + } 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_ATTRIBUTE_NODE: + break; + default: + node_list_unlink((xmlNodePtr) node->properties); + } + } + node = node->next; } } @@ -645,13 +721,12 @@ 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); + node_list_wrapper_dtor(doc->children, 0); 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; @@ -659,14 +734,15 @@ static void php_free_xml_node(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* 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) { - attr_list_wrapper_dtor(node->properties); - node_list_wrapper_dtor(node->children); + /* Attribute Nodes ccontain accessible children + attr_list_wrapper_dtor(node->properties); */ + node_list_wrapper_dtor((xmlNodePtr) node->properties, 0); + node_list_wrapper_dtor(node->children, 0); node_wrapper_dtor(node); xmlFreeNode(node); } else { node_wrapper_dtor(node); } - } static void php_free_xml_attr(zend_rsrc_list_entry *rsrc TSRMLS_DC) @@ -708,42 +784,37 @@ static void php_free_xml_parser(zend_rsrc_list_entry *rsrc TSRMLS_DC) #if HAVE_DOMXSLT -static void php_free_xslt_stylesheet(zend_rsrc_list_entry *rsrc TSRMLS_DC) -{ - xsltStylesheetPtr sheet = (xsltStylesheetPtr) rsrc->ptr; - - if (sheet) { - node_wrapper_dtor((xmlNodePtr) sheet); - xsltFreeStylesheet(sheet); - } -} - static void xsltstylesheet_set_data(void *obj, zval *wrapper) { -/* - char tmp[20]; - sprintf(tmp, "%08X", obj); - fprintf(stderr, "Adding %s to hash\n", tmp); -*/ ((xsltStylesheetPtr) obj)->_private = wrapper; } - -#ifdef HELLY_0 static zval *xsltstylesheet_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 *) (((xsltStylesheetPtr) obj)->_private)); } -#endif + +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); + } +} void *php_xsltstylesheet_get_object(zval *wrapper, int rsrc_type1, int rsrc_type2 TSRMLS_DC) { @@ -775,7 +846,7 @@ void *php_xsltstylesheet_get_object(zval *wrapper, int rsrc_type1, int rsrc_type return obj; } -static void php_xsltstylesheet_set_object(zval *wrapper, void *obj, int rsrc_type) +static void php_xsltstylesheet_set_object(zval *wrapper, void *obj, int rsrc_type TSRMLS_DC) { zval *handle, *addr; @@ -889,7 +960,7 @@ static zval *xpath_context_get_data(void *obj) return ((zval *) (((xmlXPathContextPtr) obj)->user)); } -static void php_xpath_set_context(zval *wrapper, void *obj, int rsrc_type) +static void php_xpath_set_context(zval *wrapper, void *obj, int rsrc_type TSRMLS_DC) { zval *handle, *addr; @@ -932,7 +1003,7 @@ static zval *php_xpathcontext_new(xmlXPathContextPtr obj, int *found TSRMLS_DC) */ object_init_ex(wrapper, xpathctx_class_entry); rsrc_type = le_xpathctxp; - php_xpath_set_context(wrapper, (void *) obj, rsrc_type); + php_xpath_set_context(wrapper, (void *) obj, rsrc_type TSRMLS_CC); return (wrapper); } @@ -944,7 +1015,7 @@ static void xmlparser_set_data(void *obj, zval *wrapper) } -static void php_xmlparser_set_object(zval *wrapper, void *obj, int rsrc_type) +static void php_xmlparser_set_object(zval *wrapper, void *obj, int rsrc_type TSRMLS_DC) { zval *handle, *addr; @@ -979,7 +1050,7 @@ static zval *php_xmlparser_new(xmlParserCtxtPtr obj, int *found TSRMLS_DC) MAKE_STD_ZVAL(wrapper); object_init_ex(wrapper, domxmlparser_class_entry); rsrc_type = le_domxmlparserp; - php_xmlparser_set_object(wrapper, (void *) obj, rsrc_type); + php_xmlparser_set_object(wrapper, (void *) obj, rsrc_type TSRMLS_CC); return (wrapper); } @@ -1061,7 +1132,7 @@ void *php_dom_get_object(zval *wrapper, int rsrc_type1, int rsrc_type2 TSRMLS_DC } -static void php_dom_set_object(zval *wrapper, void *obj, int rsrc_type) +static void php_dom_set_object(zval *wrapper, void *obj, int rsrc_type TSRMLS_DC) { zval *handle, *addr; @@ -1301,7 +1372,7 @@ PHPAPI zval *php_domobject_new(xmlNodePtr obj, int *found, zval *wrapper_in TSR return NULL; } - php_dom_set_object(wrapper, (void *) obj, rsrc_type); + php_dom_set_object(wrapper, (void *) obj, rsrc_type TSRMLS_CC); return (wrapper); } @@ -1462,21 +1533,29 @@ xmlDocPtr php_dom_xmlSAXParse(xmlSAXHandlerPtr sax, const char *buffer, int size return(ret); } -PHP_RINIT_FUNCTION(domxml) +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) - */ 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_domxmlattrp = zend_register_list_destructors_ex(php_free_xml_attr, NULL, "domattribute", 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); @@ -2773,6 +2852,13 @@ PHP_FUNCTION(domxml_elem_set_attribute) 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); @@ -2798,7 +2884,17 @@ PHP_FUNCTION(domxml_elem_remove_attribute) if (attrp == NULL) { RETURN_FALSE; } - xmlUnlinkNode((xmlNodePtr)attrp); + + /* 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; } /* }}} */ @@ -4166,6 +4262,30 @@ PHP_FUNCTION(domxml_new_xmldoc) } /* }}} */ +/* {{{ 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; + } + + node_list_wrapper_dtor(docp->children, 1); + node_list_wrapper_dtor((xmlNodePtr) docp->properties, 1); + /* Attribute Nodes ccontain accessible children + attr_list_wrapper_dtor(docp->properties); */ + node_wrapper_free(docp); + + RETURN_TRUE; +} +/* }}} */ + /* {{{ proto object domxml_parser([string buf[,string filename]]) Creates new xmlparser */ PHP_FUNCTION(domxml_parser) @@ -4541,14 +4661,18 @@ static int node_attributes(zval **attributes, xmlNode *nodep TSRMLS_DC) 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); - add_property_string(pattr, "value", xmlNodeGetContent((xmlNodePtr) attr), 1); +*/ + 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++; @@ -4865,9 +4989,19 @@ PHP_FUNCTION(xpath_register_ns) DOMXML_PARAM_FOUR(ctxp, id, le_xpathctxp, "ss", &prefix, &prefix_len, &uri, &uri_len); - /* set the context node to NULL - what is a context node anyway? */ 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) { @@ -4941,7 +5075,7 @@ static zval *php_xsltstylesheet_new(xsltStylesheetPtr obj, int *found TSRMLS_DC) object_init_ex(wrapper, domxsltstylesheet_class_entry); rsrc_type = le_domxsltstylesheetp; - php_xsltstylesheet_set_object(wrapper, (void *) obj, rsrc_type); + php_xsltstylesheet_set_object(wrapper, (void *) obj, rsrc_type TSRMLS_CC); return (wrapper); } diff --git a/ext/domxml/php_domxml.h b/ext/domxml/php_domxml.h index 43de9a9f4f..f35435d1b9 100644 --- a/ext/domxml/php_domxml.h +++ b/ext/domxml/php_domxml.h @@ -53,7 +53,7 @@ therefore it's easier for the script-programmers to check, what's working how Can be checked with phpversion("domxml"); */ -#define DOMXML_API_VERSION "20020814" +#define DOMXML_API_VERSION "20020815" extern zend_module_entry domxml_module_entry; #define domxml_module_ptr &domxml_module_entry @@ -73,7 +73,7 @@ PHPAPI zval *php_domobject_new(xmlNodePtr obj, int *found, zval* in TSRMLS_DC); /* directory functions */ PHP_MINIT_FUNCTION(domxml); -PHP_RINIT_FUNCTION(domxml); +PHP_MSHUTDOWN_FUNCTION(domxml); PHP_MINFO_FUNCTION(domxml); PHP_FUNCTION(domxml_version); PHP_FUNCTION(xmldoc); @@ -108,6 +108,7 @@ PHP_FUNCTION(domxml_dump_mem_file); PHP_FUNCTION(domxml_dump_node); PHP_FUNCTION(domxml_doc_validate); PHP_FUNCTION(domxml_doc_xinclude); +PHP_FUNCTION(domxml_doc_free_doc); #if defined(LIBXML_HTML_ENABLED) PHP_FUNCTION(domxml_html_dump_mem); #endif