Tuesday, July 14, 2009

Advanced PHP for Web Professionals By Christopher Cosentino Chapter 10

Overview
XML (eXtensible Markup Language) is finally in the mainstream and showing the benefits that the faithful have been spouting since XML's inception. A quick look at XML support in Apache and the Java language should be enough to affirm that XML is here to stay.

PHP has built-in support for XML in the form of an extension. Unlike other extensions, XML is compiled in by default so you should not require any additional configuration to get XML to work. The PHP XML extension uses James Clark's expat library, complete details of which can be found at www.jclark.com/xml/.

A quick look, as in Figure 10-1, at a PHP page containing the phpinfo() function should confirm that XML is enabled in your build of PHP.

Figure 10-1. Confirming XML Is Enabled Using phpinfo()

PHP's XML extension allows you to parse an XML file. You can read through an XML file following its nodes and branches and perform operations on the data contained within.

PHP's XML extension does not validate XML files. That is, it cannot compare an XML file to a DTD (Document Type Definition) and verify that the file is valid according to the DTD. Having said that, you must make sure that the XML you send to PHP is either valid or well-formed.
Creating an XML Parser
Before you can use PHP's XML extension to read XML files, you must create a parser. Since XML documents are extensible, there is no way for PHP to know what elements you are searching for in the XML. Therefore, you must tell PHP how it should parse the document. You tell PHP how to parse the document by defining a new XML parser instance and then defining element handlers and character handlers. An element handler is simply a function that runs when an element is encountered in the XML. You need to define two element handlers, one handler for when an element is encountered by PHP and another handler for when the PHP parser leaves the current element. Additionally, you must specify a handler for the character data that exists between elements. If you are a little rusty on your XML-speak, here is a short example of an XML document:

<?xml version="1.0"?>
<document>
<title>XML Is Easy</title>
<body>Demystifying XML. Read about it here.</
body>
</document>

Looks simple enough, right? That's because it is. Since you are already familiar with HTML, the basics of XML should be readily apparent. Elements are enclosed by "<" and ">". Start and end elements are differentiated by the presence or absence of the "/" symbol. Think of a first-level heading tag: <h1>A Heading</h1>. The first <h1> tag starts the element, and the second </h1> tag closes the element. The characters in between are the character data. Now this is a hugely simplified example, but if you are unfamiliar with XML, then it should shed some light on what we are about to do as we create a parser.

Defining the XML Parser
You define an XML Parser by using the xml_parser_create() function:

$parser = xml_parser_create(ENCODING);

You assign xml_parser_create() to a variable, which is passed to the other functions required for parsing XML pages. Additionally, you can optionally assign the type of encoding that the parser should use. Encoding is the character encoding in the XML document. You can choose one of three encoding types:

ISO-8859-1 (default)

US-ASCII

UTF-8

Once you have defined a new parser instance, you can then create your handlers to do the actual work of reading through an XML file.

Defining the Element Handlers
Element handlers are defined using the xml_set_element_handler() function:

xml_set_element_handler(XML_PARSER, START_FUNCTION,
END_FUNCTION);

xml_set_handler takes three arguments:

XML_PARSER— The variable that you created when you called the xml_create_parser() function.

START_FUNCTION— The name of the function to call when the parser encounters a start element.

END_FUNCTION— The name of the function to call when the parser encounters an end element.

Defining Character Handlers
Character handlers are defined using the set_character_handler() function:

xml_set_character_handler(XML_PARSER,
CHARACTER_FUNCTION);

xml_set_character_handler() takes two arguments:

XML_PARSER— The variable that you created when you called the xml_create_parser() function.

CHARACTER_FUNCTION— The name of the function to call when character data is encountered.

Starting the Parser
The final piece to the puzzle is the function that starts the whole process, xml_parse():

xml_parse(XML_PARSER, XML);

xml_parse() takes two arguments:

XML_PARSER— The variable that you created when you called the xml_create_parser() function.

XML— The XML that is to be parsed.

Cleaning Up
After you have finished parsing the document, you should free the memory holding the parser by calling the xml_parser_free() function:

xml_parser_free(XML_PARSER);

xml_parser_free() takes one argument, XML_PARSER, which is the variable that you created when you called the xml_create_parser() function.

Let's look at an example to solidify the principles we've just discussed. The following example uses all of the functions just discussed. It opens a simple XML file, aptly named simple.xml, and parses the XML within. Figure 10-2 displays the output. Here is simple.xml, which you'll need to create and place in the same directory as the xml1.php example:

Script 10-1 simple.xml
1. <?xml version="1.0"?>
2. <document>
3. <title>XML Exposed</title>
4. <body>Demystifying XML. Read about it here.</body>
5. </document>

Script 10-2 xml1.php
1. <?
2. function startElement($xml_parser, $name, $attributes) {
3. print("<p><i>Encountered Start Element For:</i>$name\n");
4. }
5.
6. function endElement($xml_parser, $name) {
7. print("<p><i>Encountered End Element For:</i>$name\n");
8. }
9.
10. function characterData($xml_parser, $data) {
11. if($data != "\n") {
12. print("<p><i>Encountered Character Data:</i>$data\n");
13. }
14. }
15.
16. function load_data($file) {
17. $fh = fopen($file, "r") or die ("<P>COULD NOT OPEN FILE!");
18. $data = fread($fh, filesize($file));
19. return $data;
20. }
21. /***** MAIN *****/
22. $file = "simple.xml";
23. $xml_parser = xml_parser_create();
24. xml_set_element_handler($xml_parser, "startElement", "endElement");
25. xml_set_character_data_handler($xml_parser, "characterData");
26. xml_parse($xml_parser, load_data($file)) or die ("<P>ERROR PARSING XML!");
27. xml_parser_free($xml_parser);
28. ?>

Figure 10-2. xml1.php

Script 10-2. xml1.php Line-by-Line Explanation LINE
DESCRIPTION

2
Create a function called startElement() to handle any start elements that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):

$xml_parser

$name

$attributes

3
Print a message to the screen when a start element is encountered.

4
End the function declaration.

6
Create a function called endElement() to handle any end elements that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):

$xml_parser

$name

7
Print a message to the screen when an end element is encountered.

8
End the function declaration.

10
Create a function called characterData() to handle the data found between elements as the script parses the XML. The function takes the following as arguments (required by PHP):

$xml_parser

$data

11–13
If there is actual character data between the elements (not just a newline character), print a message to the screen when character data is encountered.

14
End the function declaration.

16
Create a function called load_data() to read the data from an XML file into the script so that it may be parsed. The function takes one argument, $file, which is the name (with an optional path) of the XML file that you want to parse.

17
Attempt to assign a file handle to the file. If unsuccessful, kill the script and print out an error message.

18
If the file opening was successful, read in the entire file into the $data variable. Note that this is wildly inefficient for large files.

19
Return the $data variable to the calling program.

20
End the function declaration.

21
Begin the main program.

22
Assign a file name to the $file variable. Note that you can use a full path to the file, such as:

Windows: $file = "C:\winnt\xml\myxmlfile.xml";

Linux: $file = "/home/me/xml/myxmlfile.xml";

23
Create a variable called $xml_parser and assign it as a PHP xml parser using the xml_parser_create() function.

24
Define the custom start and end element handler functions that you created above using the xml_set_element_handler() function. Whenever PHP encounters a start or end element, it will use the respective function.

25
Define the custom data handler function that you created above using the xml_set_character_data_handler() function. Whenever PHP encounters character data, it will use the characterData() function.

26
Begin parsing the XML with the xml_parse function. The xml_parse() function requires the name of the XML parser ($xml_parser) and the XML data that you want to parse. In this case, we provide the XML data by using the custom load_data($file) function. If the xml_parse() function fails, then kill the script and display an error message.

27
After the xml_parse() function has completed parsing the XML, free the memory associated with the parser by using the xml_parser_free() function.
Parsing and Transforming XML Documents
The example above works well for parsing ultrasimple documents, but it doesn't take into account nested elements or attributes.

In the real world, XML documents are highly structured and precisely defined. A great deal of effort goes into designing XML information products. One reason for the amount of effort that is required is the extensible nature of XML. XML gives you a set of rules that allows you to create these complex structured documents. Those rules, however, provide for an almost infinite amount of possibilities for defining a document's structure. Keep that in mind when creating a PHP XML parsing application. Each application that you create will only work with one particular document structure. The extra effort spent designing the document structure pays off when you code applications that will parse that data.

Having said that, this next example tackles a couple of problems you will find in coding parsers for your own XML documents. These problems include multiple instances of the same element (usually differentiated by attributes or content) and attributes: two things you are sure to find in "real" XML documents. The complexity of the example document, bebop.xml, is fairly basic, but as you will see, the complexity of coding a PHP parser to read more complex documents doesn't necessarily increase; it is really just doing a lot more of the same thing over and over. The basic function of the element and character handlers does not change. You just need to add additional cases to encompass the additional variations that your document requires.

This next example shows how a more complex document can be parsed and how to present the resulting data. The XML file is the beginning of series synopsis for the popular anime title, Cowboy Bebop. Although it is far from complete as far as the series information goes, the XML structure allows for adding data to make the document more comprehensive. The output appears in Figure 10-3.

Figure 10-3. xml_series.php

The first part of the script, bebop.xml, is the actual XML file that is read into the parser.

Script 10-3 bebop.xml
1. <?xml version="1.0"?>
2. <series title="Cowboy Bebop" genre="Anime" subgenre="Science Fiction">
3. <dvd number="1">
4. <episode number="1">
5. <title>Asteroid Blues</title>
6. <synopsis>Jet and Spike track down a drug dealer.</synopsis>
7. <characters>
8. <character>Jet</character>
9. <character>Spike</character>
10. </characters>
11. </episode>
12. <episode number="2">
13. <title>Stray Dog Strut</title>
14. <synopsis>Ein Joins the crew.</synopsis>
15. <characters>
16. <character>Jet</character>
17. <character>Spike</character>
18. <character>Ein</character>
19. </characters>
20. </episode>
21. <episode number="3">
22. <title>Honkey Tonk Woman</title>
23. <synopsis>Introduction of Faye Valentine.</synopsis>
24. <characters>
25. <character>Jet</character>
26. <character>Spike</character>
27. <character>Ein</character>
28. <character>Faye</character>
29. </characters>
30. </episode>
31. <episode number="4">
32. <title>Gateway Shuffle</title>
33. <synopsis>Having fun at the casino.</synopsis>
34. </episode>
35. <episode number="5">
36. <title>Ballad Of Fallen Angels</title>
37. <synopsis>Spike's past comes back to haunt him.</synopsis>
38. </episode>
39. </dvd>
40. <dvd number="2">
41. <episode number="6">
42. <title>Sympathy For The Devil</title>
43. <synopsis>The mystery of the boy Wen.</synopsis>
44. </episode>
45. <episode number="7">
46. <title>Heavy Metal Queen</title>
47. <synopsis>Truckers in space.</synopsis>
48. </episode>
49. <episode number="8">
50. <title>Waltz for Venus</title>
51. <synopsis>Welcome to Venus.</synopsis>
52. </episode>
53. <episode number="9">
54. <title>Jamming With Edward</title>
55. <synopsis>Edward joins the crew.</synopsis>
56. <characters>
57. <character>Jet</character>
58. <character>Spike</character>
59. <character>Ein</character>
60. <character>Faye</character>
61. <character>Edward</character>
62. </characters>
63. </episode>
64. <episode number="10">
65. <title>Ganymede Elegy</title>
66. <synopsis>Homecoming for Jet.</synopsis>
67. </episode>
68. </dvd>
69.</series>

Script 10-4 xml_series.php
1. <html>
2. <head>
3. <title>XML - DVD SERIES PARSER</title>
4. <style type=text/css>
5. h1, h2, h3 {font-family: verdana, helvetica, sans-serif;}
6. p, blockquote {font-family: verdana, helvetica, sans-serif; font-size: 10pt}
7. .navy {color: navy; }
8. .characters {font-size: 8pt; color: red}
9. </style>
10. </head>
11. <body>
12. <?
13. function startElement($xml_parser, $name, $attributes) {
14. global $TagsOpen, $counter;
15. switch($name) {
16. case($name = "SERIES"):
17. $TagsOpen["SERIES"] = 1;
18. ?>
19. <h1>DVD Series</h1>
20. <h2>Title: <span><?=$attributes["TITLE"]?></span>
21. <br>Genre: <span><?=$attributes["GENRE"]?></span>
22. <br>Subgenre: <span><?=$attributes["SUBGENRE"]?></span></h2>
23. <?
24. break;
25. case($name = "DVD"):
26. $TagsOpen["DVD"] = 1;
27. ?>
28. <h3>DVD <span><?=$attributes["NUMBER"]?></span>
29. <?
30. break;
31. case($name = "EPISODE"):
32. $TagsOpen["EPISODE"] = 1;
33. ?>
34. <p><span>Episode: <?=$attributes["NUMBER"]?>
35. <?
36. break;
37. case($name = "TITLE");
38. $TagsOpen["TITLE"] = 1;
39. break;
40. case($name = "SYNOPSIS"):
41. $TagsOpen["SYNOPSIS"] = 1;
42. break;
43. case($name = "CHARACTERS"):
44. $TagsOpen["CHARACTERS"] = 1;
45. ?>
46. <blockquote>Characters:<span>
47. <?
48. break;
49. case($name = "CHARACTER"):
50. $TagsOpen["CHARACTER"] = 1;
51. $counter++;
52. break;
53. }
54. }
55.
56. function endElement($parser, $name) {
57. global $TagsOpen, $counter;
58. switch($name) {
59. case($name = "SERIES"):
60. $TagsOpen["SERIES"] = 0;
61. break;
62. case($name = "DVD"):
63. $TagsOpen["DVD"] = 0;
64. break;
65. case($name = "EPISODE"):
66. $TagsOpen["EPISODE"] = 0;
67. break;
68. case($name = "TITLE");
69. $TagsOpen["TITLE"] = 0;
70. ?>
71. </span>
72. <?
73. break;
74. case($name = "SYNOPSIS"):
75. $TagsOpen["SYNOPSIS"] = 0;
76. break;
77. case($name = "CHARACTERS"):
78. $TagsOpen["CHARACTERS"] = 0;
79. ? >
80. </span></blockquote>
81. <?
82. $counter = 0;
83. break;
84. case($name = "CHARACTER"):
85. $TagsOpen["CHARACTER"] = 0;
86. break;
87. }
88. }
89.
90. function characterData($parser, $data) {
91. global $TagsOpen, $counter;
92. switch($TagsOpen) {
93. case($TagsOpen["CHARACTER"] == 1):
94. if($counter == 1) {
95. echo " $data";
96. } else {
97. echo ", $data";
98. }
99. break;
100. case($TagsOpen["SYNOPSIS"] == 1):
101. echo "<br>$data\n";
102. break;
103. case($TagsOpen["TITLE"] == 1):
104. echo " - \"$data\"";
105. }
106. }
107.
108. function load_data($file) {
109. $fh = fopen($file, "r") or die ("<P>COULD NOT OPEN FILE!");
110. $data = fread($fh, filesize($file));
111. return $data;
112. }
113.
114. $TagsOpen = array(
115. "SERIES" => 0,
116. "DVD" => 0,
117. "EPISODE" => 0,
118. "TITLE" => 0,
119. "SYNOPSIS" => 0,
120. "CHARACTERS" => 0,
121. "CHARACTER" => 0
122. );
123.
124. $counter = 0;
125.
126. $file = "bebop.xml";
127. $xml_parser = xml_parser_create();
128. xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
129. xml_set_element_handler($xml_parser, "startElement", "endElement");
130. xml_set_character_data_handler($xml_parser, "characterData");
131. xml_parse($xml_parser, load_data($file)) or die ("<P>ERROR PARSING XML!");
132. xml_parser_free($xml_parser);
133. ?>
134. </body>
135. </html>

Script 10-4. xml_series.php Line-by-Line Explanation LINE
DESCRIPTION

1–11
Display the beginning part of the HTML page for the script, including some basic styles to format the output.

12
Begin parsing the page as PHP.

13–54
Create a function called startElement() to handle any start elements that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):

$xml_parser

$name

$attributes

14
Allow the variables $TagsOpen and $counter to be accessed and modified by this function. $TagsOpen is an array that tracks which tags are open or closed. $counter is an integer that is used for formatting purposes when displaying character data.

15–53
Create a switch statement to evaluate the value of the start element name.

16–24
Add a case to the switch statement to check if the $name variable equals "SERIES".

17
Set the $TagsOpen["SERIES"] flag to true (1), since the SERIES tag is open.

18–23
Print out some information to the screen, including the attributes of the SERIES tag, which include the title, genre, and subgenre of the series.

24
Break out of the switch statement, since we are done evaluating the $name variable.

25–30
Add a case to the switch statement to check if the $name variable equals "DVD".

26
Set the $TagsOpen["DVD"] flag to true (1), since the DVD tag is open.

27–29
Print out some information to the screen regarding the DVD tag, including its attributes.

30
Break out of the switch statement, since we are done evaluating the $name variable.

31
Add a case to the switch statement to check if the $name variable equals "EPISODE".

32
Set the $TagsOpen["EPISODE"] flag to true (1), since the EPISODE tag is open.

33–35
Print out some information to the screen regarding the EPISODE tag, including its attributes.

36
Break out of the switch statement, since we are done evaluating the $name variable.

37
Add a case to the switch statement to check if the $name variable equals "TITLE".

38
Set the $TagsOpen["TITLE"] flag to true (1), since the TITLE tag is open. We don't need to print anything for TITLE, because there are no attributes associated with this tag.

39
Break out of the switch statement, since we are done evaluating the $name variable.

40
Add a case to the switch statement to check if the $name variable equals "SYNOPSIS".

41
Set the $TagsOpen["SYNOPSIS"] flag to true (1), since the SYNOPSIS tag is open. We don't need to print anything for SYNOPSIS because there are no attributes associated with this tag.

42
Break out of the switch statement, since we are done evaluating the $name variable.

43
Add a case to the switch statement to check if the $name variable equals "CHARACTERS".

44
Set the $TagsOpen["CHARACTERS"] flag to true (1), since the CHARACTERS tag is open.

45–47
Print out some formatting that will be used when we print out the individual character names.

48
Break out of the switch statement, since we are done evaluating the $name variable.

49
Add a case to the switch statement to check if the $name variable equals "CHARACTER".

50
Set the $TagsOpen["CHARACTER"] flag to true (1), since the CHARACTER tag is open. We don't need to print anything for CHARACTER, because there are no attributes associated with this tag.

51
Increment the $counter variable, as it is used to help format the output when the individual character names are displayed.

52
Break out of the switch statement, since we are done evaluating the $name variable.

53
Close the switch statement.

54
End the function declaration.

56
Create a function called endElement() to handle any end elements that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):

$xml_parser

$name

57
Allow the variables $TagsOpen and $counter to be accessed and modified by this function. $TagsOpen is an array that tracks which tags are open or closed. $counter is an integer that is used for formatting purposes when displaying character data.

58
Create a switch statement to evaluate the value of the start element name.

59
Add a case to the switch statement to check if the $name variable equals "SERIES".

60
Set the $TagsOpen["SERIES"] flag to false (0), since the SERIES tag has been closed.

61
Break out of the switch statement, since we are done evaluating the $name variable.

62
Add a case to the switch statement to check if the $name variable equals "DVD".

63
Set the $TagsOpen["DVD"] flag to false (0), since the DVD tag has been closed.

64
Break out of the switch statement, since we are done evaluating the $name variable.

65
Add a case to the switch statement to check if the $name variable equals "EPISODE".

66
Set the $TagsOpen["EPISODE"] flag to false (0), since the EPISODE tag has been closed.

67
Break out of the switch statement, since we are done evaluating the $name variable.

68
Add a case to the switch statement to check if the $name variable equals "TITLE".

69
Set the $TagsOpen["TITLE"] flag to false (0), since the TITLE tag has been closed.

70–72
Print out the closing span, which was started when the parser found an open TITLE tag.

73
Break out of the switch statement, since we are done evaluating the $name variable.

74
Add a case to the switch statement to check if the $name variable equals "SYNOPSIS".

75
Set the $TagsOpen["SYNOPSIS"] flag to false (0), since the SYNOPSIS tag has been closed.

76
Break out of the switch statement, since we are done evaluating the $name variable.

77
Add a case to the switch statement to check if the $name variable equals "CHARACTERS".

78
Set the $TagsOpen["CHARACTERS"] flag to false (0), since the CHARACTERS tag has been closed.

79–81
Close out the blockquote and span tags that were started when the parser encountered an open CHARACTERS tag.

82
Set the $counter variable to "0". The characterData() function will use this to help display the names of the characters.

83
Break out of the switch statement, since we are done evaluating the $name variable.

84
Add a case to the switch statement to check if the $name variable equals "CHARACTER".

85
Set the $TagsOpen["CHARACTER"] flag to false (0), since the CHARACTER tag has been closed.

86
Break out of the switch statement, since we are done evaluating the $name variable.

87
Close the switch statement.

88
End the function declaration.

90
Create a function called characterData() to handle any character data that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):

$xml_parser

$data

91
Allow the variables $TagsOpen and $counter to be accessed and modified by this function. $TagsOpen is an array that tracks which tags are open or closed. $counter is an integer that is used for formatting purposes when displaying character data.

92
Create a switch statement to evaluate the value of the $TagsOpen array. By examing this array, the function can determine at which point in the XML file PHP is parsing and display the data in the proper format.

93
Add a case to the switch statement to check if the CHARACTER element tags are open. We check them to see if the CHARACTER element is open before we check if any other tags are open, since the CHARACTER element is the most deeply nested element in our XML file.

94–98
Check the value of the $counter variable. If the counter variable is set to 1, then it means that this is the first character encountered in the current node of the XML. If it is the first character, then just print the character name. If it is not the first character, then print a comma and the character name. This allows you to display the entries separated by a comma.

99
Break out of the switch statement, since we are done evaluating the $TagsOpen variable.

100
Add a case to the switch statement to check if the SYNOPSIS element tags are open. We check to see if the SYNOPSIS element is open next, because it is one node up from the deepest node.

101
If the SYNOPSIS tags are open, then print the data that exists inside SYNOPSIS tags.

102
Break out of the switch statement, since we are done evaluating the $TagsOpen variable.

103
Add a case to the switch statement to check if the TITLE element tags are open. We check them to see if the TITLE element is open next, because it is one node up from SYNOPSIS.

104
If the TITLE tags are open, then print the data that exists inside TITLE tags.

105
Break out of the switch statement, since we are done evaluating the $TagsOpen variable.

106
Close the switch statement.

Note: We do not have to check any of the other elements because none of those elements contain any actual character data in between their open and close tags. They contain only other elements, so there is no need to use this function to check for data in those elements.

107
End the function declaration.

108
Create a function called load_data() to read the data from an XML file into the script so that it may be parsed. The function takes one argument, $file, which is the name (with an optional path) of the XML file that you want to parse.

109
Attempt to assign a file handle to the file. If unsuccessful, kill the script and print out an error message.

110
If the file opening was successful, read the entire file into the $data variable. Note that this is wildly inefficient for large files.

111
Return the $data variable to the calling program.

112
End the function declaration.

114–122
Define and initialize the $TagsOpen array. You need to define an array element for each element that needs to be tracked. The array item's key is the name of the element, and the value is either 1 or 0 (true or false). Set all the values to false (0) in the beginning, since no tags are supposed to be open before you even start parsing the file.

124
Initialize the $counter variable to "0".

126
Assign a file name to the $file variable. Note that you can use a full path to the file such as:

Windows: $file = "C:\winnt\xml\myxmlfile.xml";

Linux: $file = "/home/me/xml/myxmlfile.xml";

127
Define a parser option using the xml_set_parser_option() function. This function changes options as to how the parser operates. It requires three arguments:

The XML parser that you created using the xml_parser_create function.

The option you want to set.

The value of the option you are setting.

In this case, we are setting the option "XML_OPTION_CASE_FOLDING" to true, which changes the case of element names to uppercase when the parser reads the XML file. This way, we do not have to worry if there is mixed case in the element names in the XML file. When checking to see if a particular element is encountered, you only need to check for the uppercase version of the element name.

128
Create a variable called $xml_parser and assign it as a PHP xml parser using the xml_parser_create() function.

129
Define the custom start and end element handler functions that you created above using the xml_set_element_handler() function. Whenever PHP encounters a start or end element, it will use the respective function.

130
Define the custom data handler function that you created above using the respective function. Whenever PHP encounters character data, it will use the characterData() function.

131
Begin parsing the XML with the xml_parse function. The xml_parse() function requires the name of the XML parser ($xml_parser) and the XML data that you want to parse. In this case, we provide the XML data by using the custom load_data($file) function. If the xml_parse() function fails, then kill the script and display an error message.

132
After the xml_parse() function has completed parsing the XML, free the memory associated with the parser by using the xml_parser_free() function.

133–135
Stop parsing the page as PHP and close out the HTML for the page.
Dumping Database Contents into an XML File
Although it doesn't require the PHP XML functions, dumping data from a database into an XML file using PHP is a great way to get some structured XML that you can use in other XML-enabled applications.

The nature of database data lends itself nicely to use in XML applications. You have already created a database structure for the data by specifying the table structures. Using PHP, you can easily extract those tables into an XML file, which can be stored in a text file or displayed in a browser window.

This next script extracts the contents of a database table containing address book entries and displays that information in the browser. If you have a browser such as Internet Explorer, then you can view the XML natively, as in Figure 10-4. Otherwise, you can view the browser source to see the XML.

Script 10-5 xml_dump.php
[View full width]
1. <?
2. /* SQL Table Required For This Script
3. create table xmladdressbook (
4. id INT NOT NULL,
5. first VARCHAR(32),
6. last VARCHAR(32),
7. phone VARCHAR(12),
8. email VARCHAR(64),
9. address VARCHAR(64),
10. city VARCHAR(32),
11. state VARCHAR(2),
12. zip VARCHAR(5),
13. type VARCHAR(16)
14. primary key(id));
15.
16. insert into xmladdressbook values (1, 'Neil','Armstrong','555-555-1234','neil@example.
com','1 Moon Road','Lunar City','LN', '99999', 'Friend');
17. insert into xmladdressbook values (2, 'Buzz','Aldrin','555-555-1235','buzz@example.com',
'2 Moon Road','Lunar City','LN', '99999', 'Business');
18. */
19.
20. function connect() {
21. if(!$db = @mssql_pconnect("localhost","mssqluser","password")) {
22. print("<h1>Cannot Connect to the DB!</h1>\n");
23. return 0;
24. } else {
25. mssql_select_db("php", $db);
26. return 1;
27. }
28. }
29.
30. if(connect()) {
31. print('<?xml version="1.0"?>');
32. ?><addressbook><?
33. $sql = "select * from xmladdressbook order by last, first";
34. $result = mssql_query($sql);
35. while($row = mssql_fetch_array($result)) {
36. ?>
37. <contact type="<?=$row["type"]?>">
38. <name>
39. <last_name><?=$row["last"]?></last_name>
40. <first_name><?=$row["first"]?></first_name>
41. </name>
42. <details>
43. <phone><?=$row["phone"]?></phone>
44. <email><?=$row["email"]?></email>
45. <address><?=$row["address"]?></address>
46. <city><?=$row["city"]?></city>
47. <state><?=$row["state"]?></state>
48. <zip><?=$row["zip"]?></zip>
49. </details>
50. </contact>
51. <?
52. }
53. ?></addressbook><?
54. }
55. ?>

Figure 10-4. xml_dump.php shown in Internet Explorer

Script 10-5. xml_dump.php Line-by-Line Explanation LINE
DESCRIPTION

2–14
Use these lines to create the table in your SQL database.

16
Create an entry in the database.

17
Create an additional entry in the database.

20–28
Create a function called connect(). This function is used to create a connection to the database.

21
Attempt to connect to the database, in this case, an MS SQL database. (You can replace all the MS SQL functions with MySQL functions simply by changing the first "s" in mssql to a "y"—"mysql").

22
If the script cannot connect to the database, then print an error.

23
Return false (0) to the calling program, because the database connection failed.

24–25
If the connection attempt was successful, then select the database on the database server that the script will use.

26
Return true (1) to the calling program, because the connection was successful.

27
Close the if statement started on line 12.

28
End the function declaration.

30
If the connect() function returns true, then continue on to the next line.

31
Print out the XML required to begin an XML page. You need to use the print statement to enclose the native <? and ?> XML processing instructions so that they are not interpreted to be PHP tags.

32
Print the top level XML tag to the screen. Since this is an address book, the top level tag is <addressbook>.

33
Generate an SQL statement to select all of the contents of the table and order the results by last name, then first name.

34
Execute the SQL query and place the result in the $result variable.

35–52
Loop through the result set with a while loop.

36–51
Stop processing the page as PHP. Since we are printing out a lot of data, it's easier to just have the server send out plain XML instead of processing each line as a print statement. Print out each of the results returned in the current row as part of a "contact" element. The subelements found within "contact" contain the details of the address book entry.

52
Close the while loop.

53
Close out the XML by printing out a close tag for the root element <addressbook>.

54
Close the if statement started on line 30.

55
End the script.

0 comments: