]> granicus.if.org Git - php/commitdiff
Big Patch with a lot of mem-leak fixes
authorChristian Stocker <chregu@php.net>
Tue, 8 Apr 2003 18:26:50 +0000 (18:26 +0000)
committerChristian Stocker <chregu@php.net>
Tue, 8 Apr 2003 18:26:50 +0000 (18:26 +0000)
- 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

ext/domxml/php_domxml.c
ext/domxml/php_domxml.h

index 5df30520d0a23d51e9c988f2e4e33f31e8f312c3..4db699dbc6b5d677ce7397a44559b724674be7af 100644 (file)
@@ -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);
 }
index 43de9a9f4fae53b4b285e6cb0cb3c0a3d6ff33c8..f35435d1b9f9e2065bda3559cb73805eb860a1cd 100644 (file)
@@ -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