PHP Performance – How to Speed Up XML Parsing Code

performancePHPxml

I'm developing a web application and we are using XML as a database and I'm using Linux as a platform and centos 6.5 as a server. Changing some value would make a lot a lot of changes in about six XML files.

So here's the problem: Pressing button save after editing, adding, deleting takes about one or one and half minutes or more! This is because my code has to update about six XML files, log files and the crontab file. I wonder if there's any thing similar to threads in PHP?

I have my functions and all code I want, I just want to know how php can be used in a new way to make performance better. Can you guide me even by links to read?

I don't know what's exactly this problem is called to search about it. I'm still a beginner.

here's the update function I'm using :

 public static function update($tableName, $fieldsToUpdate, $newValues, $value = NULL) {
        //Load XML file
        $fileName = G_APP_SCRIPTS_PATH . "/XMLS/xmls/" . $tableName . ".xml";
        $xml = new DOMDocument;
        $xml->load($fileName);
        //Load XSD file
        $xsdfileName = G_APP_SCRIPTS_PATH . "/XMLS/xsds/" . $tableName . ".xsd";
        $xsd = new DOMDocument;
        $xsd->load($xsdfileName);
        //Generate Xpath
        $path = c_XML::generateXPath($xml, $xsd, $fieldsToUpdate, $value);
        $xpath = new DOMXPath($xml);
        //Query nodes to update
        $nodes = $xpath->query($path);
//        print_r($nodes->item(0));
        //If number of new values < number of elements to update,means we have to update
        //all specified elements in the XML without any condition
        if (count($newValues) < $nodes->length) {
            for ($i = 0; $i < $nodes->length / count($newValues); $i++)
                $newValues = array_merge($newValues, $newValues);
        }
        //Iterate over the nodes to update
        for ($i = 0; $i < $nodes->length; $i++) {
            $oldNode = $nodes->item($i);
            for ($index = 0; $index < count($fieldsToUpdate); $index++) {
                //Find the associated index in the $fieldsToUpdate array
                if (strcmp($oldNode->nodeName, $fieldsToUpdate[$index]) === 0) {
                    break;
                }
            }
            //Create new node with the old node's name
            $newNode = $xml->createElement($oldNode->nodeName);
            //var_dump($newNode);
            //if the new value is an array,means we have to update an inner node
            if (is_array($newValues[$index])) {
                //go to the desired node in XSD and get its children elements,
                //to know their format
                $xsdxpath = new DOMXPath($xsd);
                $children = $xsdxpath->query("/xs:schema/xs:element/xs:complexType/xs:sequence/xs:element/xs:complexType/xs:sequence/xs:element[@name='" . $oldNode->nodeName . "']/xs:complexType/xs:sequence/xs:element");
                //convert the $chlidren node list to an array to pass the createNode function.
                for ($c = 0; $c < $children->length; $c++) {
                    $childrenArray[$c] = $children->item($c);
                }
                //create the desired node
                c_XML::createNode($xml, $xsdxpath, $newNode, $childrenArray, $newValues[$index]);
            }
            //If the new value is not an array
            else
                $newNode->nodeValue = $newValues[$index];
            //Replace old node with new node
            $parent = $oldNode->parentNode;
            $parent->replaceChild($newNode, $oldNode);
        }
        //Validate & save XML
        $isValid = c_XML::validateXMLtoXSD($xml, $xsdfileName);
        if ($isValid == 1) {
            $xml->save($fileName);
            return true;
        }
        else
            return false;
    }

and when user update a value I use this way in updating other xml files :

  private static function change_value_in_xml_file($old_value, $new_value) {
            $all_nodes = c_XML_h::select(tables::xmlfile, '*');
           $nodes = array();
            for ($i = 0; $i < count($all_nodes); $i++) {
               $nodes[$i] = array();
//just get the nodes i want
               $nodes[$i][0] = $all_nodes[$i][0];
               $nodes[$i][1] = $all_nodes[$i][3];
            }
            //find old value and replace it with new value
            foreach ($nodes as $node) {
                $newValues[0] = $new_value;

                if ($node[1] == $old_value) {
                    $id_to_update = $node[0];
                    $fieldsToSelect[0] = xmlfile:: value;
                    c_XML_h::update(tables::xmlfile, $fieldsToSelect, $newValues, $id_to_update);
                }

            }
        }

Best Answer

XML is not a very good choice for a database that needs a lot of updating, because text files are not random access: you must parse the whole file every time you want to read or write a single element. You might be able to improve your code slightly, but XML isn't going to be fast.

Some other problems you may have:

  • This will scale very poorly. As the size of your database increases, your code will get much, much slower.
  • Concurrent updates will be a problem. Could you have two people performing actions that will update the database at the same time? If so, at best one action would fail to complete. At worst, it could leave your data in a corrupt state.

I would suggest you bite the bullet and replace it with a real database. You appear to have nicely abstracted your data access into functions, so all you have to do is rewrite those functions to access a database rather than read and write XML files.

(Also keep around the old XML functions to aid in moving the data. Once you have written the database interface, you can make a little program that will read all of your data in XML using the old functions, then write it to the database using the new functions.)

Related Topic