Search This Blog

Monday, September 13, 2010

PHP4 XML Functions

PHP5 maintains backward compatibility with many PHP4 features, so we'll start off this section discussing some of the PHP4 XML functions before moving on to the new XML functions available in PHP5. The XML parser functions found in PHP4 implement James Clark's expat (a parser for XML 1.0 written in the C language). Expat parsers can tell you if an XML document is well-formed, but do not validate XML documents, so parsers created using these functions must receive well-formed XML documents or an error will be generated (but fortunately you can find out where the error occurred).


Here are a few of the most common functions:

  • xml_parser_create: This is the basic function to create an xml parser, which can then be used with the other XML functions for reading and writing data, getting errors, and a variety of other useful tasks. Use xml_parser_free to free up the resource when done.




  • xml_parse_into_struct: Parse XML data into an array structure. You can use this function to take the contents of a well-formed XML file, turn it into a PHP array, and then work with the contents of the array.




  • xml_get_error_code: Gets XML parser error code (defined as constants, such as XML_ERROR_NONE and XML_ERROR_SYNTAX). Use xml_error_string to get the textual description of the error based on the error code.




  • xml_set_option: There are several options that can be set for an xml parser: XML_OPTION_CASE_FOLDING and XML_OPTION_TARGET_ENCODING. The case folding option is enabled by default, and means that element names will be made uppercase unless it is disabled. Target encoding enables you to specify which encoding is used for the target; the default is the encoding used by xml_parser_create, which in turn is ISO-8859-1. Use xml_parser_get_option to find out what options are currently set for an xml parser.



There are also a number of other xml parser-related functions for setting up handlers of various types (for common xml components, such as processing instructions, character data, and so on).


Try it Out: Create XML Document
Start example
In this example, you use ordinary strings and PHP string-handling functions to create an XML document. Interestingly, this works (in a very crude way) without any PHP XML functions at all. You'll capture input for names and values of several elements and attributes, and then use the data to create an XML document, then store it as a file. The drawback, of course, is that you have very little capability to create or work with a document of arbitrary complexity; we can only have two elements with two attributes each, and no validation. But, sometimes, all you need to do is output a little bit of XML, and as we demonstrate here you can do it without any XML functions.


To make this example work, you need to create a subfolder (under your Web folder containing this file) named xml_files. You'll store the XML files you create and read from in there (be sure to properly specify your default directory, $default_dir, if it differs from the example's). Start your HTML editor and enter the following code, saving it as php_xml.php:


<html>
<head>
<title>PHP XML Functions</title>
<meta http-equiv="Content-Type" content="text/html; charsets=iso-8859-1">
</head>
<body bgcolor="#FFFFFF">
<form method="POST" action="php_xml.php">
<input type="hidden" name="posted" value="true">
<table width="100%" border="1" cellpadding="10">
  <tr><td><h2>Using PHP XML Capabilities</h2>
<?php
if (isset($_POST['posted'])) {
   $cmdButton = $_POST['cmdButton'];
   $root_element_name = $_POST['root_element_name' ];
   $element01_name = $_post['element01_name'];
   $att0101_name = $_POST['element01-name'];
   $att0101-name = $_POST['att0101_name'];
   $att0101_value = $_POST['att0101_value'];
   $att0102_name = $_POST['att0102_name'];
   $att0102_value = $_POST['att0102_value'];
   $element0101_name = $_POST['element0101_name'];
   $att010101_name = $_POST['att010101_name'];
   $att010l01_value = $_POST[att010101_value];
   $att010102_name = $_POST['att010102_name'];
   $att010102_value = $_POST['att010102_value'];
   switch ($cmdButton) {
      case "Create XML Document";
         //format an xml document
         $xml_dec = "<?xml version-\"1.0\" encoding=\"UTF-8\"?>";
         $doc_type_dec = "";
         $root_element_start = "<" . $root_element_name . ">";
         $root_element_end = "</" . $root_element_name . ">";
         $xml_doc = $xml_dec;
         $xml_doc = $doc_type_dec;
         $xml_doc .= $root_element_start;
         $xml_doc .= "<" . $element01_name . "="
          . $att0101_name . "-" . "'" , $att0101_value . "'" . " "
          . $att0102_name . "=" . "'" . $att0102_value . "'" . ">";
         $xml_doc .= "<" . $element0101_name . " "
          . $att010101_name . "=" . "'" . $att010101_value . "'" . " "
          . $att010102_name . "=" , "'". $att010102_value . "'" . ">";
         $xml_doc = "</" . $element0101name . ">";
         $xml_doc .- "</" . $element0l_name . ">";
         $xm1_doc .= $root_element_end;
         //open a file and copy the xml text into it, then close it
         $default_dir = "/Inetpub/wwwroot/beginning_php5/ch08/xml_files";
         $fp = fopen($default_dir . "/" . $_POST['xml_file_name'] . ".xml", w);
         $write = fwrite($fp, $xml_doc);
         $response_message = "XML document created";
         break;
      default;
         break;
   }
}
?>

<table width="100%" border="1"><tr>
   <td><font face="Arial, Helvetica, sans-serif" size="-1">
   <b>Create a Well-formed XML Document</b></font></td>
   </tr><tr><td><table width="100%" border="1"><tr>
   <td colspan="3"><font size="-1"><b><font face="Arial, Helvetica,
   sans-serif">Response =
   <?php echo $response_message; ?></font></b></font></td>
   </tr><tr>
   <td><font size="-1"><b><font face="Arial, Helvetica,
   sans-serif ">Element or Attribute</font></b></font></td>
   <td><font size="-1"><b><font face="Arial, Helvetica,
   sans -serif ">Name</font></b></font></td>
   <td><font size="-1"><b><font face="Arial, Helvetica,
   aans-serif ">Value</font></b></font></td>
   </tr><tr>
   <td><font size="-1"><b><font face="Arial, Helvetica,
   sans-serif">Root Element:</font></b></font></td>
   <td><input type="text" name="root_element_name">
   </td><td>&nbsp;</td></tr><tr>
   <td><font size="-1"><b><font face="Arial, Helvetica,
   sans-serif">Element01:</font></b></font></td>
   <td><input type="text" name="element01_name">
   </td><td>&nbsp; </td></tr><tr>
   <td><font size="-1"><b><font face="Arial, Helvetica,
   sans-serif ">Attribute0101:</font></b></font></td>
   <td><input type="text" name="att0101_name">
   </td><td><input type="text" name="att0101_value">
   </td></tr><tr>
   <td> <font size="-1"><b><font face="Arial, Helvetica,
   sans-serif">Attribute0102:</font></b></font></td>
   <td><input type="text" name="att0102_name">
   </td><td><input type="text" name="att0102_value">
   </td></tr><tr><td>
   <font size="-1"><b><font face="Arial, Helvetica, sans-serif">Element0101:
   </font></b></font></td>
   <td><input type="text" name="element0101_name">
   </td><td>&nbap;</td></tr><tr>
   <td><font size="-1"><b><font face="Arial, Helvetica,
   sans-serif">Attribute010101:</font></b>/font></td>
   <td><input type="text" name="att010101_name">
   </td><td><input type="text" name="att010101_value">
   </td></tr><tr>
   <td><font sizes"-1"><b><font face="Arial, Helvetica,
   sans-serif">Attribute010102:</font></b></font></td>
   <td><input type="text" name="att010102_name">
   </td><td><input type=" text" name="att010102_value">
   </td></tr><tr>
   <td><b><font face="arial, Helvetica, sans serif:">
   Current XMI, Files</font></b><hr>

   <?php
      $default_dir = "/Inetpub/wwwroot/beginning_php5/ch08/xml_files";
      if (!($dp = opendir($default_dir))) {
         die ("Cannot open $default_dir.");
      }
      while ($file=readdir($dp)) {
         if ($file != '.' && $file != '..') {
             echo "$file<HR>";
         }
      }
      closedir ($dp);
      ?>
   <font size="-1"><b><font face="Arial, Helvetica, sans-serif">Name
   of new XML Document File:</font></b></font>
   </td><td colspan="2" valign="bottom">
   <input type="text" name="xml_file_name" size="30">
   </td></tr><tr><td>&nbsp; </td>
   <td colspan= 2">
   <input type="submit" name="cmdButton" vaue= "Create XML Document:">
   </td></tr></table></td></tr>  </table>
   </td></tr></table>
</form>
</body>
</html>

Once you've saved the file as php_xml.php, run it, and use the form to create a well-formed XML document.

first_xml.xml is the name of the first XML document created with this form. If you use the same element names and values, first_xml.xml will have the following contents once it is created:


<?xml version="1.0" encoding="UTF-8"?><root_element><element01 att0101="att one
val" att0102="att two val"><element0101 att010101="att one one val'' att010102=
"att one two val"></element0101></element01></root_element>

Open the.xml file (first_xml.xml or whatever you named it). 

How it Works

This is a standard Web page made from HTML. It displays a table with a form that enables the user to enter names and values for a fixed set of XML elements and attributes. When the form is submitted, the isset() function allows us to determine that fact, and the code runs. After capturing the values from the $_POST variable, a switch case block is then used to decide which block of code to run. Although there's only one choice in the switch case block now, it's there so you can add more choices to it later if you want:



<?php
if (isset($_POST['posted'])) {
   $cmdButton = $_POST['cmdButton'];
   $root_element_name = $ POST['root_element_name'];
   $element01_name = $_POST['element01_name'];
   $att0101_name = $_POST['att0101_name'];
   $att0101_value = $ POST['att0101_value'];
   $att0102_name = $_POST['att0102_name'];
   $att0102_value = $ POST['att0102_value'];
   $element0101_name = $_POST['element0101_name'];
   $att010101_name = $_POST['att010101_name'];
   $att010101_value = $POST['att010101_value'];
   $att010102_name = $_POST['att010102_name'];
   $att010102_value = $_POST['att010102_value'];
   switch ($cmdButton) {
   case "Create XML Document";

For this example the code block formats the data provided by the user into a string containing all the proper components of a well-formed XML document:


//format an xml document
         $xml_dec = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
         $doc_type_dec = " ,
         $root_element_start = "<" . $root_element_name . ">";
         $root_element_end = "</" . $root_element_name . ">";

         $xml_doc = $xml_dec . "\n";
         $xml_doc .= $doc_type_dec . "\n";
         $xml_doc .= $root_element_start . "\n";
         $xml_doc .= "<" . $element01_name . " "
          . $att0101_name . "=" . "'" . $att0101_value . "'" . " "
          . $att0102_name . "=" . "'" . $att0102_yalue . "'" . "> " . " \n";
         $xml_doc .= "<" . $element0101_name . " "
          . $att010101_name . "=" . "'" . $att010101_value . "'" . " "
          . $att010102_name . "=" . "'" . $att010102_value . "'" . ">" . "\n";
         $xml_doc .= "</" . $element0101_name . ">\n";
         $xml_doc .= "</" . $elament01_name . ">\n";
         $xml_doc .= $root_element_end;

Next, a file is opened and the XML document is written to the file (you need to properly specify the default directory, and yours may differ from this example's):


//open a file and copy the xml text into it, then close it
$default_dir = "/Inetpub/wwwroot/beginning_php5/ch08/xml_files";
$fp = fopen($default_dir . "/" . $_POST['xml_file_name'] . ".xml", w);
$write = fwrite($fp, $xml_doc);

Finally, a response is created and the code block ends with a break:


        $response_message = "XML document created";
        break;

XML Parsers

Now you've seen how to create an XML document using nothing more than ordinary PHP string functions, but it should be clear that these functions provide no easy way to manipulate your XML document. You could write some regular expressions and special functions of your own to make the job easier, but of course, the authors of PHP realized that and came up with some for you.


The next example demonstrates the use of the xml_parser_create() and xml_parse_into_struct() functions. It also uses the file_get_contents() function to retrieve the contents of a file and turn it into a string.


Try it Out: Read an XML Document
Start example
For this example, we'll make a copy of the php_xml.php file and name it php_xml02.php. Then we'll copy a new Switch Case block in, and a new table for displaying the results.

  1. Make a copy of php_xml.php, save the copy as php_xml02.php, and open it in your HTML editor.




  2. Enter the following code:
    if (isset($_POST[posted])) {
    
       $xml_file_chosen = $_POST[xml_file_chosen];
       $cmdButton = $_POST[cmdButton];
    
       switch ($cmdButton) {
    




  3. Cut this code and paste it in place of the old section beginning with if (isset and ending with switch ($cmdButton).




  4. Now enter the following code and cut and paste it over the old case section:
    case "Parse an XML Document";
    
       //find the file specified
       $default_dir = "/Inetpub/wwwroot/beginning_php5/ch08/xml_flies";
       $xml_string = file_get_contents
       ($default_dir . "/" . $xml_file_chosen,"rb");
    
       // Read our existing data and turn it into arrays
       $xp = xml_parser_create();
       xml_parse_into_struct($xp, $xml_string, $values, $keys);
       xml_parser_free($xp);
    
      break;
    
    




  5. Create the following HTML table and then cut and paste it over the old table:


    <table width="100%" border="1">
       <tr><td><font face="Arial, Helvetica, sans-serif" size="-1">
    <b>Parse an XML Document</b></font></td></tr><tr><td>
       <table width="100%" border="1"><tr><td>
       <font face="Arial, Helvetica, sans-serif" size="-1">
    <b>Choose XML File</b></font></td></td>
       <td><select name="xml_file_chosen">
    
          <?php
          $default_dir = "/Inetpub/wwwroot/beginning_php5/ch08/xml_files";
          if (!($dp = opendir($default_dir))) {
             die("Cannot open $default_dir.");
          }
          while ($file = readdir($dp)) {
             if ($file != '.' && $file != '..') {
                echo "<option value='$file'>$file</option>\n";
             }
          }
          closedir($dp);
          ?>
    
          </select></td></tr><tr>
          <td><font face="Arial, Helvetica, sans-serif" size="-1">
          <b>XML File Contents</b></font><hr>
    
          <?php
          if ($cmdButton == "Parse an XML Document") {
             echo "Keys array<BR><BR><PRE>";
             print_r($keys);
             echo "</PRE><BR><BR>Values array<BR><BR><PRE>";
             print_r($values);
             echo "</PRE>";
          }
          ?>
    </td><td>
    <input type="submit" name="cmdButton" value="Parse an XML Document">
    </td></tr></table>
    </td></tr></table>
    




  6. Save the file, and then give it a try. Remember to change the form's action attribute to the new filename, php_xm102.php. You should see the XML file you created using the original php_xml.php script listed in the drop-down box (and others, if you created several). Select it and click the Submit button.  

End example

How it Works

The code starts the case block by finding the file you've selected, and then opens the file and reads it as a string into the variable $xml_string. Next, it uses the xml_parser_create function to make a new parser object (named $xp), and the xml_parse_into_struct() function to turn the XML document into a set of arrays, one for the keys and one for the values.

As you can see, the arguments the xml_parse_into_struct() function takes the xml parser (stored in a variable named $xp), the string variable (containing the XML document as a string), and the names of the two arrays you want to create:


//find the file specified
$default_dir = "/Inetpub/wwwroot/beginning_php5/ch08/xml_files";
$xml_string = file_get_contents($default_dir . "/" . $xml_file_chosen,"rb");

// Read our existing data and turn it into arrays
$xp = xml_parser_create();
xml_parse_into_struct($xp, $xml_string, $values, $keys);
xml_parser_free($xp);
break;

The code for the HTML table simply provides a means of choosing the .xml file to parse. Then, if the button has been clicked, it uses the print_r() function to print the two array structures to the screen. It's the HTML <PRE> tags that make the data display nicely:


<table width="100%" border="1">
   <tr><td><font face="Arial, Helvetica, sans-serif" size="-1">
<b>Parse an XML Document</b></font></td></tr><tr><td>
   <table width="100%" border="1"><tr><td>
     <font face="Arial, Helvetica, sans-serif" size="-1">
<b>Choose XML File</B></font></td></td>
   <td><select name="xml_file_chosen">

      <?php
      $default_dir = "/Inetpub/wwwroot/beginning_php5/ch08/xml_files";
      if (!($dp = opendir($default_dir))) {
         die("Cannot open $default_dir.");
      }
      while ($file = readdir($dp)) {
         if ($file != '.' && $file != '..') {
            echo "<option value='$file'>$file</option>\n";
         }
      }
      closedir($dp);
      ?>

      </select></td></tr><tr>
      <td><font face="Arial, Helvetica, sans-serif" size="-1">
      <b>XML File Contents</b></font><hr>

      <?php
      if ($cmdButton == "Parse an XML Document") {
         echo "Keys array<BR><BR><PRE>";
         print_r($keys);
         echo "</PRE><BR><BR>Values array<BR><BR><PRE>";
         print_r($values);
         echo "</PRE>";
      }
      ?>

</td><td>
<input type="submit" name="cmdButton" value="Parse an XML Document">
</td></tr></table>
</td></tr></table>

The Document Object Model

The Document Object Model (DOM) is simply a hierarchical model for interacting with documents. It enables access to the parts of a document by addressing them directly via their lineage in the document.


You can model XML documents with the DOM because there is a specific relationship among the parts of any XML document. As you've seen, there can be only one root element, and it is the parent of all the rest of the elements in an XML document. This means that the root element is at the bottom (hence the name) of the hierarchy or tree from which the rest of the elements spring. Therefore, the relationship between the components of an XML document can be inferred programmatically (and that's just what the PHP DOM extension functions do; we'll talk about this more in just a bit).


For any given element, elements inside it are its children (or child elements), whereas the element it is inside of is its parent (or parent element). So you can think of elements as parents and children, or you can think of elements as being like a tree, with root, branches, and leaves. Within the DOM, both ways of thinking about XML document components are valid. Elements and other components of an XML document are considered to be nodes within the DOM.

The DOM Extension

The DOM extension follows the Worldwide Web Consortium's DOM Level 2 recommendation closely. This recommendation states, "the DOM is an application programming interface (API) for valid HTML and well-formed XML documents." With the DOM, any well-formed XML document can be programmatically built, navigated, and nodes added, edited, and deleted.


Configuring PHP with with-dom=dom_dir makes this extension available. For PHP on Windows, copy libxml2.dll or iconv.dll to the System32 folder. So please see the documentation for further instructions.


Support for any DOM extension functions is still experimental (which is why there are no examples in this book), but you can refer to Professional PHP Programming (Wrox) for a more in-depth explanation of the PHP DOM extension.

Using the PHP DOM Extension Functions

PHP's DOM functions include domxml_new_doc() to create new XML documents, domxml_open_file() to open an XML document file as a DOM object, and domxml_open_mem() to create a DOM object from an XML document already in memory. These functions return a DOM object, not a string or other common data type. When you use the PHP DOM extension functions, you typically first create a DOMDocument object and then manipulate it using functions that are part of the class of that object. There are a number of object classes available, and by starting with a DOMDocument object, you can then examine it and retrieve new objects reflecting XML document components such as elements, attributes, and so on.


For example, to open the XML file created earlier, you might use code like this to create a DOM object named $my_dom_obj:


$my_dom_obj = domxml_open_file("first_xml.xml");

Then, to create a variable representing the root element found within the document, you might use code like this:


$the_root_element = $dom->document_element();

You can also manipulate the DOM object you've created with quite a few other PHP DOM functions, including create_element(), which creates a new element, and append_child(), which appends an element as a child of an existing element.


If you want to find a particular element in an XML document that has been arranged by the DOM, for example, you could use the get_element_by_tagname function of the DOMElement object to find that element as a node in the DOM, and it would be found as a child node within the hierarchy of root, branch, leaf in the DOM.


There are many other classes of objects available in the PHP DOM extension, as well as quite a few functions for using them to create and manipulate XML documents. Their object-oriented nature makes them more suitable for a book such as Professional PHP Programming, to which you can refer for further information about the DOM and PHP.