Web Programming for Business - PHP Object-Oriented Programming with Oracle

5 XML in Action

Overview

XML is structured in a simple and common format (in plain text) and provides a platform-neutral, efficient way of sharing and storing data. As such, an XML document can be shared by computers with completely different components, interfaces, configurations, and operating systems.

Two techniques for working with XML are presented. First, the ‘SimpleXMLElement’ class is introduced, explained, and implemented as a means to convert XML into an object that can be processed with normal property selectors and array iterators. Second, XPath is introduced, explained, and implemented as a means of finding information in an XML document.

Learning Objectives

After completing this chapter, you will gain a fundamental understanding of the topic by creating, modifying, traversing, and saving XML content. The following objectives summarize the skills the chapter will help you develop:

1.    Learn the definition of XML and its characteristics.

2.    Learn the five basic reasons why you should use XML.

3.    Learn the structure of an XML document.

4.    Learn the rules that ensure that an XML document is well-formed.

5.    Learn about the ‘SimpleXML’ extension and ‘SimpleXMLElement’ class.

6.    Learn two ways to create an instance of the ‘SimpleXMLElement’ class.

7.    Learn about the ‘foreach’ construct.

8.    Learn how to create and save an XML document.

9.    Learn how to display the XML tree of an XML document.

10.Learn how to display and save XML document content.

11.Learn how to modify an XML document.

12.Learn how to remove nodes and values from an XML document.

13.Learn how to add attributes and elements to an XML document.

14.Learn how to use ‘XPath’ with XML.

XML

Extensible markup language (XML) is a standard for describing how information is structured. Specifically, XML is a text-based markup language that is the standard for data interchange on the web. It provides a foundation for creating documents that can be used on a wide variety of platforms and for storing data in a simple, common format in plain text.

The ubiquitous capacity and common format of XML allow computers to share information even if they have completely different system components, configurations, and operating systems. XML allows people to create their own customized markup applications for exchanging information.

XML Tags

XML uses tags in the same way as HTML. Unlike HTML, XML does not have a fixed range of tags and attributes. Within certain rules, you can name tags and attributes whatever you want unless you are working on a large collaborative project that needs to use a standardized vocabulary. The big difference is that an HTML tag tells you how a page is organized, while an XML document contains no information about the layout or structure of a page.

XML tags cannot include any whitespace or punctuation other than the hyphen ‘-’, underscore ‘_’ or period ‘.’. Tags cannot begin with ‘xml’ in any combination of uppercase or lowercase letters.

XML Data

XML stores data in a hierarchical (tree) structure according to meaning and without any reference to web page presentation. That is, XML provides an elegant way to separate data from format. An XML document may not even represent a page or even exist as a physical file on the server.

An XML document can be generated dynamically by a database in response to an incoming request. The server analyzes the request, queries the database to get the most current information, and sends the result to the recipient formatted as XML.

Five Basic Reasons to Use XML

XML is almost ubiquitous in terms of information sharing between computer systems. The five basic reasons for this ubiquity include simplicity, organization, accessibility, standardization, and reusability.

Simplicity is provided by XML because it is easy to create and understand. Such simplicity allows you freedom to develop based on your needs.

Organization is provided by XML because it allows you to build your website by segmenting the design process. Data is on one page, while formatting rules are on another. Once you have a general idea of what information you need to produce, you can write the data page first and then work on the design. XML thereby allows you to produce your website in stages and stay organized in the process.

Accessibility is provided by XML by enabling you to compartmentalize your work. Separating data (from format) makes it accessible when changes are needed without impacting format. If you write both segments (data and format) in HTML, you create sections that incorporate the formatting instructions with the information you need to display on the page. When changes are needed, you must wade through all the code to find a few lines because data and format are not separated. In addition, you risk causing unforeseen errors because any change can potentially impact the format since both segments are in the same place. Separating data from format thereby makes changes easy, less error prone, and less time consuming.

Standardization is provided by XML because it is an international standard. As such, anyone in the world has the ability to view and understand your document.

Reusability is provided by XML because you can make one data page and use it over and over again. That is, you can create as many display pages as you want for that data. XML allows you to generate different styles and formats based on one page of information.

XML Structure

Seeing the contents of an XML document is a good way to understand its basic structure. The following XML document ‘cars.xml’ contains three car elements.

 1 <?xml version="1.0" encoding="ISO-8859-1"?>

 2 <cars>

 3 <identity sno="fm12345">

 4 <make>Ford</make>

 5 <model>Mustang</model>

 6 </identity>

 7 <identity sno="ha98764">

 8 <make>Honda</make>

 9 <model>Accord</model>

10 </identity>

11 <identity sno="mm11111">

12 <make>Mazda</make>

13 <model>Miata</model>

14 </identity>

15 </cars>

Create text file ‘cars.xml’ on your file system (i.e., Linux) for use in an upcoming example. The XML declaration (sometimes referred to as the XML prolog) in line 1 tells browsers and processors that the document is XML. If used, the declaration must be the first entry in the document. There cannot be anything else before it, not even blank lines or comments. The root element ‘<cars>’ (lines 2 and 15) contains everything else inside the document. An element describes the data that it contains, and can also contain other elements and attributes. An attribute provides additional information about elements, but cannot contain other elements.

At the next level are three ‘<identity>’ elements (lines 3–6, 7–10, and 11–14), each of which has a ‘sno’ attribute (lines 3, 7, and 11). Within each ‘<identity>’ element are two elements named ‘<make>’ and ‘<model>’, which each contain text. The text inside an element is referred to as a text element (or text node).

XML Nodes

XML can define seven nodes – root, element, attribute, text, comment, processing instructions, and namespace. The root node contains everything in the XML document, including the root element. An element node can contain other element nodes, a text node, or be empty. An attribute node is a name–value pair in an element’s opening tag. A text node is the literal text between element opening and closing tags. A comment node contains comments. A processing instruction node passes instructions to the application processing an XML document. A namespace nodeprovides a way to prevent clashes between elements from different sources.

XML Rules

An XML document must be well-formed to be considered viable. A well-formed XML document must conform to the seven rules of XML syntax. First, it can only have one root element. Second, every start tag must have a matching closing tag. Third, empty elements can omit the closing tag, but if they include one it must have a forward slash before the closing angle bracket ‘/>’. Fourth, elements must be properly nested. Fifth, attribute values must be in quotes. Sixth, in the content of an element or attribute value ‘<’ and ‘&’ must be replaced by HTML entities ‘<’ and ‘&’, respectively. Seventh, an XML declaration is allowed only at the start of the document.

SimpleXML

The ‘SimpleXML’ extension provides a very simple and easily usable toolset to convert XML to an object that can be processed with normal property selectors and array iterators. When using the ‘SimpleXML’ extension, all created objects are instances of the ‘SimpleXMLElement’ class.

The ‘SimpleXML’ extension converts an XML document into an object in the following manner. Elements are converted into single attributes of the ‘SimpleXMLElement’ object (or ‘SimpleXML’ object for short). When there is more than one element on one level, they are placed inside an array. Attributes are accessed using associative arrays, where an index corresponds to the attribute name. Text data from elements are converted into strings. If an element has more than one text node, they are arranged in the order they are found.

The ‘SimpleXML’ extension is fast and easy when performing basic tasks, such as reading XML files, extracting data from XML strings, and editing text nodes or attributes. However, when dealing with advanced XML, like namespaces, it is better to use XML DOM (a standard way of accessing and manipulating XML documents).

Create an Instance of the ‘SimpleXMLElement’ Class

The easiest way to create an instance of the ‘SimpleXMLElement’ class is to load the XML document from a file with ‘simplexml_load_file()’. If you need to work with an advanced object, use ‘simplexml_import_dom()’ to return an instance of ‘SimpleXML’ from a DOM object.

 1 <?php

 2 // File load_simplexml.php

 3 $file= 'cars.xml';

 4 $xml = simplexml_load_file($file);

 5 foreach($xml->identity as $id)

 6 {

 7 echo $id['sno'] . ' ' .

 8 $id->make . ' ' . $id->model;

 9 echo '<br />';

10 }

11 ?>

The ‘simplexml_load_file()’ construct creates an instance of the ‘SimpleXMLElement’ class based on ‘cars.xml’ and places it in the ‘$xml’ variable (line 4). The ‘foreach()’ construct (lines 5–10) iterates over the newly created object. As such, the ‘sno’ attribute (which uniquely identifies each car), make, and model are displayed for each ‘<identity>’ element (lines 7–9).

Load PHP file ‘load_simplexml.php’ in a web browser. Figure 5.1 displays the results.

Figure 5.1

Figure 5.1 Display of XML from ‘cars.xml’ Using ‘simplexml’

A second way to create an instance of ‘SimpleXMLElement’ is to create an instance of the ‘DOMDocument’ class and convert it to ‘SimpleXML’.

 1 <?php

 2 // File load_dom.php

 3 $xml = 'cars.xml';

 4 $dom = new DOMDocument;

 5 $dom->load($xml);

 6 $xml = simplexml_import_dom($dom);

 7 foreach($xml->identity as $id)

 8 {

 9 echo $id['sno'] . ' ' .

10 $id->make . ' ' . $id->model;

11 echo '<br />';

12 }

13 ?>

First, I create an instance of DOM and place it in ‘$dom’ (line 4). Second, I load ‘cars.xml’ into the DOM object (line 5). Third, I create a ‘SimpleXML’ object from the DOM object (line 6). The ‘foreach()’ construct (lines 7–12) iterates through the XML document and displays the results.

Load PHP file ‘load_dom.php’ in a browser. Figure 5.2 displays the results.

Figure 5.2

Figure 5.2 Display of XML from ‘cars.xml’ Using ‘DOM’

Foreach

The ‘foreach()’ construct provides an easy way to iterate over arrays and objects. For objects, the syntax is ‘foreach (‘list’ as ‘object in list’)’. When working with XML, the list holds the contents of the XML document, and the object in list is the current element in the XML document.

When ‘foreach()’ commences, the internal object pointer automatically resets to the first object in the list. With each loop, the value of the current object from ‘list’ is assigned to ‘object in list’ and the internal object pointer is advanced by one. Be careful to use ‘object in list’ within the loop rather than ‘list’. Using ‘list’ inside the loop causes a fatal error.

Create and Save an XML Document

If you use an earlier version of ‘Google Chrome’ web browser, you may have to download ‘XML Tree’ to enable XML data to be displayed in a user-friendly way. The software is free and easy to install.

PHP file ‘stereo_xml.php’ creates an XML document and displays it. It does not save it. I always display results before saving to ensure that the code works and XML is created as expected.

 1 <?php

 2 // File stereo_xml.php

 3 $newXML = new SimpleXMLElement('<root></root>');

 4 $stereo1 = $newXML->addChild('stereo');

 5 $stereo1->addAttribute('id','ow191');

 6 $stereo1->addChild('name','Joyous Sound');

 7 $stereo1->addChild('preamp','Spectral');

 8 $stereo1->addChild('amp','Linn');

 9 $stereo1->addChild('cdp','Nagra');

10 $stereo1->addChild('speakers','Opera');

11 $stereo1->addChild('wire','Wireworld');

12 $stereo1->addChild('wire','JPS Labs');

13 $stereo1->addChild('wire','Shunyata');

14 $stereo1->addChild('price','$20,000.00');

15 $stereo2 = $newXML->addChild('stereo');

16 $stereo2->addAttribute('id','ms299');

17 $stereo2->addChild('name','Nirvana');

18 $stereo2->addChild('preamp','Belles');

19 $stereo2->addChild('amp','Belles');

20 $stereo2->addChild('cdp','Esoteric');

21 $stereo2->addChild('speakers','Focal');

22 $stereo2->addChild('wire','Shunyata');

23 $stereo2->addChild('price','$10,000.00');

24 $stereo3 = $newXML->addChild('stereo');

25 $stereo3->addAttribute('id','jj101');

26 $stereo3->addChild('name','Heaven');

27 $stereo3->addChild('preamp','MBL');

28 $stereo3->addChild('amp','MBL');

29 $stereo3->addChild('cdp','MBL');

30 $stereo3->addChild('speakers','MBL');

31 $stereo3->addChild('wire','MIT');

32 $stereo3->addChild('wire','Nordost');

33 $stereo3->addChild('price','$120,000.00');

34 $stereo4 = $newXML->addChild('stereo');

35 $stereo4->addAttribute('id','dp530');

36 $stereo4->addChild('name','Ear Candy');

37 $stereo4->addChild('preamp','AudioPax');

38 $stereo4->addChild('amp','Linn');

39 $stereo4->addChild('cdp','Linn');

40 $stereo4->addChild('cdp','Esoteric');

41 $stereo4->addChild('speakers','Energy');

42 $stereo4->addChild('wire','Crystal');

43 $stereo4->addChild('wire','JPS Labs');

44 $stereo4->addChild('wire','Wireworld');

45 $stereo4->addChild('wire','Shunyata');

46 $stereo4->addChild('price','$30,000.00');

47 header ('Content-Type: text/xml');

48 echo $newXML->asXML();

49 ?>

I start by creating an instance of the ‘SimpleXMLElement’ class and include the ‘root’ node (line 3). I continue by using the ‘addChild()’ method (line 4) to add a child element. Next, I use the ‘addAttribute’ method (line 5) to add an identifier to the ‘<stereo>’ element. The remaining lines for ‘$stereo1’ (lines 6–14) complete the first stereo system.

The same logic is used to create the remaining stereo systems. Notice that some systems have multiple ‘wire’ elements (e.g., ‘stereo1’ → lines 11–13) and one system (i.e., ‘stereo4’ → lines 39 and 40) has two ‘cdp’ entries.

The ‘header’ line (line 47) informs the browser that content is to be displayed. The final line (line 48) uses the ‘asXML()’ method to actually display the content. Load ‘stereo_xml.php’ to see the results.

Before saving the XML document, create a directory (e.g., ‘xml_docs’) to hold it and ensure that the permissions of the directory are set to allow ‘writing’. I use ‘777’ permissions (which gives full permissions on the folder) to save the XML document because I want to make sure that the save works. However, consult your IT expert, in case more restrictive permissions are deemed appropriate.

mkdir xml_docs

chmod 777 xml_docs

The next example modifies ‘stereo_xml.php’ to save the XML document. Modifications include removing the last two lines and adding six new ones. Open a new PHP file ‘stereo_xml_save.php’ and copy the contents of ‘stereo_xml.php’ into it.

 1 <?php

 2 // File stereo_xml_save.php

 3 $newXML = new SimpleXMLElement('<root></root>');

 4 $stereo1 = $newXML->addChild('stereo');

 5 $stereo1->addAttribute('id','ow191');

 6 $stereo1->addChild('name','Joyous Sound');

 7 $stereo1->addChild('preamp','Spectral');

 8 $stereo1->addChild('amp','Linn');

 9 $stereo1->addChild('cdp','Nagra');

10 $stereo1->addChild('speakers','Opera');

11 $stereo1->addChild('wire','Wireworld');

12 $stereo1->addChild('wire','JPS Labs');

13 $stereo1->addChild('wire','Shunyata');

14 $stereo1->addChild('price','$20,000.00');

15 $stereo2 = $newXML->addChild('stereo');

16 $stereo2->addAttribute('id','ms299');

17 $stereo2->addChild('name','Nirvana');

18 $stereo2->addChild('preamp','Belles');

19 $stereo2->addChild('amp','Belles');

20 $stereo2->addChild('cdp','Esoteric');

21 $stereo2->addChild('speakers','Focal');

22 $stereo2->addChild('wire','Shunyata');

23 $stereo2->addChild('price','$10,000.00');

24 $stereo3 = $newXML->addChild('stereo');

25 $stereo3->addAttribute('id','jj101');

26 $stereo3->addChild('name','Heaven');

27 $stereo3->addChild('preamp','MBL');

28 $stereo3->addChild('amp','MBL');

29 $stereo3->addChild('cdp','MBL');

30 $stereo3->addChild('speakers','MBL');

31 $stereo3->addChild('wire','MIT');

32 $stereo3->addChild('wire','Nordost');

33 $stereo3->addChild('price','$120,000.00');

34 $stereo4 = $newXML->addChild('stereo');

35 $stereo4->addAttribute('id','dp530');

36 $stereo4->addChild('name','Ear Candy');

37 $stereo4->addChild('preamp','AudioPax');

38 $stereo4->addChild('amp','Linn');

39 $stereo4->addChild('cdp','Linn');

40 $stereo4->addChild('cdp','Esoteric');

41 $stereo4->addChild('speakers','Energy');

42 $stereo4->addChild('wire','Crystal');

43 $stereo4->addChild('wire','JPS Labs');

44 $stereo4->addChild('wire','Wireworld');

45 $stereo4->addChild('wire','Shunyata');

46 $stereo4->addChild('price','$30,000.00');

47 $dom = new DomDocument();

48 $dom->preserveWhiteSpace = false;

49 $dom->formatOutput = true;

50 $dom->loadXML($newXML->asXML());

51 $dom->save('xml_docs/stereo.xml');

52 echo 'XML Document Saved';

53 ?>

Start by removing the ‘header’ (line 47) and ‘echo’ (line 48) statements. Add line 47 to create a new DOM object. Add line 48 to disallow any whitespace with the ‘preserveWhiteSpace’ method. Add line 49 to turn on formatting with the ‘formatOutput’ method. Add line 50 to load ‘SimpleXML’ output into the DOM object with the ‘loadXML’ method. The parameter ‘$newXML->asXML()’ inside ‘loadXML()’ actually creates the XML output. Add line 51 to save the XML document with the ‘save’ method. Finally, add line 52 to display that the document was saved.

Load file ‘stereo_xml_save.php’ in a browser to save the XML document. Figure 5.3 indicates that the document was saved.

Figure 5.3

Figure 5.3 Display Showing that XML Was Saved

Just to be sure, check the contents of ‘stereo.xml’ in the ‘xml_docs’ directory. To accomplish this, change to the ‘xml_docs’ directory, issue list command ‘ls’, and edit the xml document as shown.

cd xml_docs

ls

vi stereo.xml

The contents of ‘stereo.xml’ should exactly match the following code.

<?xml version="1.0"?>

<root>

<stereo id="ow191">

<name>Joyous Sound</name>

<preamp>Spectral</preamp>

<amp>Linn</amp>

<cdp>Nagra</cdp>

<speakers>Opera</speakers>

<wire>Wireworld</wire>

<wire>JPS Labs</wire>

<wire>Shunyata</wire>

<price>$20,000.00</price>

</stereo>

<stereo id="ms299">

<name>Nirvana</name>

<preamp>Belles</preamp>

<amp>Belles</amp>

<cdp>Esoteric</cdp>

<speakers>Focal</speakers>

<wire>Shunyata</wire>

<price>$10,000.00</price>

</stereo>

<stereo id="jj101">

<name>Heaven</name>

<preamp>MBL</preamp>

<amp>MBL</amp>

<cdp>MBL</cdp>

<speakers>MBL</speakers>

<wire>MIT</wire>

<wire>Nordost</wire>

<price>$120,000.00</price>

</stereo>

<stereo id="dp530">

<name>Ear Candy</name>

<preamp>AudioPax</preamp>

<amp>Linn</amp>

<cdp>Linn</cdp>

<cdp>Esoteric</cdp>

<speakers>Energy</speakers>

<wire>Crystal</wire>

<wire>JPS Labs</wire>

<wire>Wireworld</wire>

<wire>Shunyata</wire>

<price>$30,000.00</price>

</stereo>

</root>

Notice that the XML document is saved in a user-friendly format. Using DOM in PHP file ‘stereo_xml_save.php’ allows finer formatting control over the XML document. DOM was unnecessary in PHP file ‘stereo_xml.php’ because the browser takes care of formatting when displaying XML. XML document ‘stereo.xml’ is used in this chapter to illustrate the flexibility of ‘SimpleXML’.

Display the Tree of an XML Document

Examining the XML tree of a document shows how the document is structured, which makes extraction of desired data very easy. The next example uses PHP file ‘display_tree.php’ to display the XML tree when loaded in a browser.

1 <?php

2 // File display_tree.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 echo '<pre>';

5 print_r($xml);

6 echo '</pre>';

7 ?>

I start by converting ‘stereo.xml’ into a ‘SimpleXML’ object and placing the object into ‘$xml’ (line 3). I continue by dynamically generating preformatted text with ‘<pre>’ (line 4), printing the object in human-readable form with ‘print_r()’ (line 5), and ending preformatted text with ‘</pre>’ (line 6).

Load ‘display_tree.php’. Figure 5.4 shows the first node of the tree. Notice that data is stored in multidimensional arrays.

Figure 5.4

Figure 5.4 Display of Tree Structure from ‘stereo.xml’

Since the XML document has many stereo systems, ‘stereo’ is an array of ‘SimpleXML’ objects beginning with an index of zero. Each stereo contains the ‘@attributes’ array with ‘id’ as the index. ‘@attributes’ must be an array because each element can have more than one attribute. For the first stereo system, the ‘id’ is ‘ow191’. Each stereo system also includes several elements, including ‘name’, ‘preamp’, ‘amp’, ‘cdp’, ‘speakers’, ‘wire’, and ‘price’. Since the ‘wire’ element of the first stereo system has more than one value, it becomes an array with three elements – ‘Wireworld’, ‘JPS Labs’, and ‘Shunyata’.

Knowing the tree structure of even the most complex XML document facilitates easy access to elements and attributes. It also helps debug problems because the XML tree shows how content is stored in the system.

Display the Content of an XML Document

The ‘SimpleXML’ extension enables easy access to XML content. The XML tree shows that each element node ‘<stereo>’ is a ‘SimpleXML’ object. Since each ‘<stereo>’ has child nodes, you can access them as properties.

PHP file ‘stereo_names.php’ displays ‘name’ elements of the first two stereo systems. Line 3 converts ‘stereo.xml’ into a ‘SimpleXML’ object and loads it into ‘$xml’. The ‘name’ of the first stereo system is displayed (line 4) with index ‘0’ and a pointer to ‘name’. The name of the second stereo system is then displayed (line 5) with an index of ‘1’ and a pointer to ‘name’. The XML tree (see Figure 5.4) shows that the first stereo system is element zero and the second is element one.

1 <?php

2 // File stereo_names.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 echo $xml->stereo[0]->name . '<br />';

5 echo $xml->stereo[1]->name;

6 ?>

Figure 5.5

Figure 5.5 Display of First and Second Stereo ‘names’

Load ‘stereo_names.php’ in a browser. Figure 5.5 shows the results.

PHP file ‘all_names.php’ displays ‘name’ elements of all four stereo systems when loaded in a browser. Figure 5.6 shows the results.

1 <?php

2 // File all_names.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 foreach($xml->stereo as $stereo)

5 { echo $stereo->name . '<br />'; }

6 ?>

Figure 5.6

Figure 5.6 Display of All Stereo ‘names’

The ‘foreach’ construct (lines 4 and 5) uses ‘$xml->stereo’ to identify the stereo systems in the XML document, which enables easy traversal.

PHP file ‘specific_nodes.php’ displays the second and third ‘wire’ elements of the fourth stereo system when loaded in a browser. Figure 5.7 shows the results.

1 <?php

2 // File specific_nodes.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 echo $xml->stereo[3]->wire[1] . '<br />';

5 echo $xml->stereo[3]->wire[2] . '<br />';

6 ?>

Figure 5.7

Figure 5.7 Display of Fourth Stereo System Second and Third ‘wires’

Since the fourth stereo system (see Figure 5.4) has multiple ‘wires’, I use indexing to access the second and third wire elements (lines 4 and 5).

PHP file ‘all_values.php’ displays ‘name’, ‘preamp’, ‘amp’, ‘cdp’, ‘speakers’, ‘wire’, and ‘price’ elements of all stereo systems. Figure 5.8 displays the results.

 1 <?php

 2 // File all_values.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach($xml->stereo as $stereo)

 5 {

 6 echo '<em>' . $stereo->name . '</em> ';

 7 echo '<strong>' . $stereo['id'] . '</strong> ';

 8 echo $stereo->preamp . ', ' . $stereo->amp;

 9 $num = count($stereo->cdp);

10 $i = 1;

11 echo ', <<';

12 foreach($stereo->cdp as $cdp)

13 {

14 if($i < $num)

15 { echo $cdp . ', '; }

16 elseif($i == $num)

17 { echo $cdp; }

18 $i++;

19 }

20 echo '>>, ';

21 echo $stereo->speakers . ', ';

22 $num = count($stereo->wire);

23 $i = 1;

24 echo '(';

25 foreach($stereo->wire as $wire)

26 {

27 if($i < $num)

28 { echo $wire . ', '; }

29 elseif($i == $num)

30 { echo $wire; }

31 $i++;

32 }

33 echo '), and ' . $stereo->price . '<br />';

34 }

35 ?>

Figure 5.8

Figure 5.8 Display of Various Elements of All Stereo Systems

The ‘outer’ foreach()’ loop (lines 4–34) traverses all ‘stereo’ systems within the ‘SimpleXML’ object. The code inside this loop is complex because the ‘<cdp>’ and ‘<wire>’ elements can have more than one entry per stereo system. To find the exact number of these elements, I use the ‘count()’ function and assign the result to ‘$num’ (lines 9 and 22). To traverse the ‘<cdp>’ and ‘<wire>’ elements, I use a ‘foreach()’ for both.

The first ‘inner’ ‘foreach()’ (lines 12–19) traverses the ‘cdp’ array. The logic inside is a bit complex for aesthetics only. That is, I place a comma after each ‘cdp’, except for the final one. I accomplish this logic by creating a counter ‘$i’ and initialize it to ‘1’ (line 10). Inside the loop, I check to see if the counter is less than ‘$num’ (line 14). If so, I display the CD player followed by a comma ‘,’ (line 15). If not, I display the CD player without a comma ‘,’ (lines 16 and 17). I end the loop by incrementing the counter (line 18).

The second ‘inner’ ‘foreach()’ (lines 25–32) traverses the ‘wire’ array. Like the first ‘inner’ loop, I create a counter ‘$i’ and initialize it to ‘1’ (line 23). Inside the loop, I check to see if the counter is less than ‘$num’ (line 27). If so, I display the CD player followed by a comma ‘,’ (line 28). If not, I display the CD player without a comma ‘,’ (lines 29 and 30). I end the loop by incrementing the counter (line 31).

PHP file ‘names_wires.php’ displays ‘name’ and ‘wire’ for each system. The code is pretty simple because I just display everything on its own line.

 1 <?php

 2 // File names_wires.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach($xml->stereo as $stereo)

 5 {

 6 echo '<strong>' . $stereo->name . '</strong><br />';

 7 foreach($stereo->wire as $wire)

 8 { echo $wire . '<br />'; }

 9 echo '<br />';

10 }

11 ?>

The ‘outer’ ‘foreach()’ (lines 4–10) traverses all stereo systems. The ‘inner’ ‘foreach()’ (lines 7 and 8) displays all ‘wires’ within a stereo system. Load the file in a browser. Figure 5.9 shows the results.

Figure 5.9

Figure 5.9 Display of All ‘names’ with Corresponding ‘wires’

PHP file ‘elements_attributes.php’ displays ‘name’, ‘id’ attribute, and ‘wire’ for each system when loaded in a browser. Figure 5.10 shows the results.

 1 <?php

 2 // File elements_attributes.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach($xml->stereo as $stereo)

 5 {

 6 echo '<strong>' . $stereo->name . '</strong>';

 7 echo '<strong style="color:green;">';

 8 echo '  ' . $stereo['id'];

 9 echo '</strong></br />';

10 foreach($stereo->wire as $wire)

11 { echo $wire . '<br />'; }

12 echo '<br />';

13 }

14 ?>

Figure 5.10

Figure 5.10 Display of All ‘names’, ‘wires’, and ‘id’ Attributes

The ‘outer’ ‘foreach()’ (lines 4–13) traverses all stereo systems. I start by displaying the name of the system (line 6). The opening tag of each ‘<stereo>’ element contains an attribute called ‘id’. So, I use ‘$stereo['id']’ to display the value of this attribute (line 8). The ‘inner’ ‘foreach()’ (lines 10 and 11) displays all ‘wires’ within a stereo system.

PHP file ‘attributes.php’ displays the name of the third stereo system and its corresponding ‘id’ attribute. Figure 5.11 shows the results. Since the index of the first node is zero, an index of ‘2’ is used to access the third element and attribute (lines 5 and 6).

1 <?php

2 // File attributes.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 echo 'Stereo System: ';

5 echo $xml->stereo[2]->name;

6 echo ' (' . $xml->stereo[2]['id'] . ')';

7 ?>

Figure 5.11

Figure 5.11 Display of Third ‘name’ and ‘id’ Attributes

PHP file ‘names_attributes.php’ displays all stereo system names and corresponding ‘id’ values with a ‘foreach()’ loop (lines 4–8). Figure 5.12 shows the results.

1 <?php

2 // File names_attributes.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 foreach($xml->stereo as $stereo)

5 {

6 echo $stereo->name . ' (' . $stereo['id'] . ')';

7 echo '<br />';

8 }

9 ?>

Figure 5.12

Figure 5.12 Display of All ‘names’ and ‘id’ Attributes

PHP file ‘specific_wire.php’ displays all names, corresponding ‘id’ attribute values, and the first ‘wire’ of each stereo system with a ‘foreach()’ loop (lines 4–8). Figure 5.13 shows the results.

1 <?php

2 // File specific_wire.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 foreach($xml->stereo as $stereo)

5 {

6 echo $stereo->name . ' (' . $stereo['id'] . ')';

7 echo ' ' . $stereo->wire[0] . '<br />';

8 }

9 ?>

Figure 5.13

Figure 5.13 Display of All ‘names’ and ‘id’ Attributes with First ‘wire’

PHP file ‘see_xml.php’ displays node information for the second stereo system. The ‘header’ statement (line 4) informs the browser to display content as XML and the ‘asXML()’ method (line 5) returns a well-formed XML string based on the SimpleXML element. Figure 5.14 shows the results.

1 <?php

2 // File see_xml.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 header('Content-Type: text/xml');

5 echo $xml->stereo[1]->asXML();

6 ?>

Figure 5.14

Figure 5.14 Display of Second Stereo System as XML

PHP file ‘see_all_xml.php displays the entire contents of the XML document. As in the previous example, the ‘header’ statement (line 4) informs the browser to display content as XML and the ‘asXML()’ method (line 5) returns a well-formed XML string based on the SimpleXML element.

1 <?php

2 // File see_all_xml.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 header('Content-Type: text/xml');

5 echo $xml->asXML();

6 ?>

Save the Content of an XML Document

If you encounter a problem when trying to save a file, the reason may be related to inadequate file permissions. We already changed file permissions earlier in the chapter to ensure that files can be saved. It doesn’t hurt, however, to check file permissions again. If the file permissions will not allow you to save, use the ‘chmod’ Linux command as follows:

chmod 777 xml_docs

PHP file ‘save_xml.php’ saves a copy of the XML document into file ‘stereo_copy.xml’. Figure 5.15 shows the results. Just to be sure, check the contents of ‘stereo_copy.xml’ in the ‘xml_docs’ directory.

1 <?php

2 // File save_xml.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 if(@$xml->asXML('xml_docs/stereo_copy.xml'))

5 { echo 'XML saved'; }

6 else

7 { echo 'Could not save XML'; }

8 ?>

Figure 5.15

Figure 5.15 Display Showing that Copy of Stereo Was Saved

The ‘asXML()’ method returns an XML document, from a ‘SimpleXML’ object, as a string. When used without an argument, the method returns a string containing XML of the current object. When used with a file as an argument, the XML is saved to the file (line 4). I precede the condition inside the ‘if’ statement with the ‘@’ symbol in line 4 to suppress any error messages that might be generated.

To save a portion of an XML document, load the document, and use ‘file_put_contents()’. PHP file ‘save_stereo2.php’ saves the second stereo system XML in ‘stereo2.xml’. Figure 5.16 shows that the document was saved.

 1 <?php

 2 // File save_stereo2.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 $output = "<?xml version='1.0' encoding='utf-8'?>\n";

 5 $output .= $xml->stereo[1]->asXML();

 6 if(file_put_contents('xml_docs/stereo2.xml',$output))

 7 { echo 'XML saved'; }

 8 else

 9 { echo 'Could not save XML'; }

10 ?>

Figure 5.16

Figure 5.16 Display Showing that Second Stereo System Was Saved

Line 4 assigns a string that identifies a file as XML (prolog) to ‘$output’. Line 5 appends the contents of the second stereo system to ‘$output’. That is, the first line of ‘$output’ is the prolog, and the lines that follow are the contents of the second stereo system.

Verify that the contents of ‘stereo2.xml’ reflect the appropriate data from the second stereo system with the following commands.

cd xml_docs

vi stereo2.xml

First, change to the ‘xml_docs’ directory. Then edit the file ‘stereo2.xml’. Figure 5.17 shows the results.

Figure 5.17

Figure 5.17 Display of XML in File Structure

When saving small XML output, the code in PHP file ‘save_stereo2.php’ works fine. However, when saving a large amount of XML data, spacing issues can make it less readable. One solution is to use DOM.

PHP file ‘save_stereo_dom.php’ uses the ‘DOMDocument’ object to format the output. Methods ‘preserveWhiteSpace()’ and ‘formatOutput()’ clean the XML document. Method ‘loadXML()’ loads the data into DOMDocument object and method ‘save()’ saves the file. Figure 5.18 shows the results.

 1 <?php

 2 // File save_stereo_dom.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 $output = "<?xml version='1.0' encoding='utf-8'?>\n";

 5 $output .= $xml->stereo[1]->asXML();

 6 $xml = simplexml_load_string($output);

 7 $file = "xml_docs/stereo_dom.xml";

 8 if(is_writable(dirname($file)))

 9 {

10 $dom = new DomDocument();

11 $dom->preserveWhiteSpace = false;

12 $dom->formatOutput = true;

13 $dom->loadXML($xml->asXML());

14 $dom->save($file);

15 echo "XML saved";

16 }

17 else

18 { echo "Cannot save XML"; }

19 ?>

Figure 5.18

Figure 5.18 Display Showing that Refined XML Was Saved

Specifically, I begin by converting ‘stereo.xml’ into a ‘SimpleXML’ object (line 3). Next, I create string output of the prolog followed by the contents of the second stereo system and assign to ‘$output’ (lines 4 and 5). I continue by loading string ‘$output’ into ‘$xml’ as a ‘SimpleXML’ object (line 6). I assign the name of the file I will create to ‘$file’ (line 7). I then check if the directory is ‘writable’ (line 8). If so, I create a new ‘DOMDocument’ object (line 10), format it (lines 11 and 12), load the object that holds the second stereo system (line 13), and save it (line 14).Figure 5.19 shows the contents of file ‘stereo_dom.xml’.

Figure 5.19

Figure 5.19 Display of Refined XML in File Structure

Comparing Figure 5.19 with Figure 5.17, notice that spacing in the XML document ‘stereo_dom.xml’ (Figure 5.19) removes whitespace before ending tag ‘</stereo>’. Even though spacing issues in this case are minor, as more data is added, readability becomes more problematic. Although ‘stereo_dom.xml’ is readable, it is not well-formed because it does not have a root element that contains the XML data (review the ‘XML Rules’ section earlier in the chapter for details).

Well-Formed XML

PHP file ‘well_formed.php’ creates a new XML document with a root element container populated with stereo systems two and four from ‘stereo.xml’. Since I added a root element container, the document is now well-formed! Load ‘well_formed.php’ in a browser. Figure 5.20 shows the results.

 1 <?php

 2 // File well_formed.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 $output = "<?xml version='1.0' encoding='utf-8'?>\n";

 5 $output .= "<root>\n";

 6 $output .= $xml->stereo[1]->asXML();

 7 $output .= $xml->stereo[3]->asXML();

 8 $output .= "\n</root>";

 9 if(file_put_contents('xml_docs/stereo_data.xml',$output))

10 { echo 'XML saved'; }

11 else

12 { echo 'Could not save XML'; }

13 ?>

Figure 5.20

Figure 5.20 Display Showing that Well-Formed Second and Fourth XML Elements Were Saved

I begin by creating a prolog string (line 4) and assigning it to ‘$output’. I continue building ‘$output’ by adding an opening ‘root’ tag (line 5). Next, I add the second and fourth stereo systems to ‘$output’ (lines 6 and 7). I finish ‘$output’ by adding a closing ‘root’ tag (line 8). I save the contents of ‘$output’ to the ‘stereo_data.xml’ file (line 9).

It is always a good idea to verify that a file was saved properly. The following Linux commands will help you do this.

cd xml_docs

vi stereo_data.xml

Tags ‘<root>’ and ‘</root>’ should contain (surround) the ‘stereo’ nodes that contain the data. Now that ‘stereo_data.xml’ is well-formed, it can be converted to a ‘SimpleXML’ object and easily traversed.

PHP file ‘traverse.php’ loads ‘stereo_data.xml’, converts it to a ‘SimpleXML’ object, traverses the object, and displays some data. Load ‘traverse.php’ into a browser. Figure 5.21 shows the results.

1 <?php

2 // File traverse.php

3 $xml = simplexml_load_file('xml_docs/stereo_data.xml');

4 foreach($xml->stereo as $stereo)

5 {

6 echo $stereo->name . ' ' . $stereo->price;

7 echo '<br />';

8 }

9 ?>

Figure 5.21

Figure 5.21 Display Showing Traversal of Well-Formed Elements

I load ‘stereo_data.xml’ and convert it into a ‘SimpleXML’ object (line 3). I then use a ‘foreach()’ loop (lines 4–8) to traverse the stereo systems in the XML document. Lines 6 and 7 display the ‘name’ and ‘price’ of the two systems.

PHP file ‘well_formed.php’ created a well-formed document (stereo_data.xml), but the spacing is still not quite right. Specifically, the spacing for element ‘<stereo>’ is not consistent. DOM can be used to fix the problem.

PHP file ‘cleanse.php’ recreates ‘stereo_data.xml’ from the original ‘stereo.xml’ and uses DOM to format the data properly. Figure 5.22 shows the results. Edit ‘stereo_data.xml’ in the ‘xml_docs’ directory to verify contents.

 1 <?php

 2 // File cleanse.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 $output = "<?xml version='1.0' encoding='utf-8'?>\n";

 5 $output .= "<root>\n";

 6 $output .= $xml->stereo[1]->asXML();

 7 $output .= $xml->stereo[3]->asXML();

 8 $output .= "\n</root>";

 9 $xml = simplexml_load_string($output);

10 $file = "xml_docs/stereo_data.xml";

11 if(is_writable(dirname($file)))

12 {

13 $dom = new DomDocument();

14 $dom->preserveWhiteSpace = false;

15 $dom->formatOutput = true;

16 $dom->loadXML($xml->asXML());

17 $dom->save($file);

18 echo "XML saved";

19 }

20 else

21 { echo "Cannot save XML"; }

22 ?>

Figure 5.22

Figure 5.22 Display Showing that Refined Well-Formed Elements Were Saved

I begin by loading ‘stereo.xml’ into ‘$xml’ as a ‘SimpleXML’ object (line 3). I continue by building ‘$output’ (lines 4–8). Next, I load string ‘$output’ into ‘$xml’ as a ‘SimpleXML’ object (line 9). I can overwrite ‘$xml’ with data from the new object because all necessary data from ‘stereo.xml’ was already extracted into ‘$output’. I create ‘$file’ (line 9) to hold file information for saving the data. If the directory is ‘writable’, I use ‘DOMDocument’ methods to format data properly before saving (lines 13–17).

Modify an XML Document

‘SimpleXML’ objects can be modified in three ways. First, values of text and attributes can be changed. Second, attribute or element nodes can be removed. Third, new nodes and attributes can be added.

PHP file ‘change.php’ reduces the price of each stereo system by 10 percent and displays results to the browser. Nothing is saved.

 1 <?php

 2 // File change.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach ($xml->stereo as $stereo)

 5 {

 6 $reduced = substr($stereo->price, 1);

 7 $reduced = str_replace(",","",$reduced);

 8 $reduced = $reduced * .9;

 9 $stereo->price = '$' . number_format($reduced,2);

10 }

11 header ('Content-Type: text/xml');

12 echo $xml->asXML();

13 ?>

Since ‘price’ is a string, it must first be converted to a number. So, I remove the dollar sign from each ‘price’ element value with ‘substr()’ (line 6). Next, I remove the comma with ‘str_replace()’ (line 7). I then reduce ‘price’ by ‘10%’ (line 8). I convert ‘price’ back into a string (line 9) by concatenating a dollar sign to the properly formatted number (decimals and a comma between grouped thousands) via the ‘number_format()’ function. Finally, I display results to a browser (lines 11 and 12).

The XML document is not actually changed because it is not saved. Figure 5.23 shows a partial display of the results (price of ‘Joyous Sound’ is reduced to $18,000).

Figure 5.23

Figure 5.23 Display Showing Partial Results from Modified XML

The following code modifies only the first stereo system. The ‘price’ of the system is reduced by ‘15%’, the ‘id’ is changed, and the name of the speakers is changed. Load the file in a browser. Figure 5.24 shows the changes made to the first stereo system.

 1 <?php

 2 // File change_specific.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 $reduced = substr($xml->stereo[0]->price, 1);

 5 $reduced = str_replace(",","",$reduced);

 6 $reduced = $reduced * .85;

 7 $xml->stereo[0]->price = '$' . number_format($reduced,2);

 8 $xml->stereo[0]['id'] = 'fw191';

 9 $xml->stereo[0]->speakers = 'Focal';

10 header ('Content-Type: text/xml');

11 echo $xml->asXML();

12 ?>

Figure 5.24

Figure 5.24 Display Showing Partial Results from Again Modified XML

I begin by removing ‘$’ from the ‘price’ of the first stereo system (line 4). I continue by removing the comma (line 5) and reducing ‘price’ by ‘15%’ (line 6). Next, I concatenate a dollar sign to the properly formatted number (line 7). I change the ‘id’ (line 8) and ‘speakers’ (line 9) of the first stereo system. I finish by displaying contents to a browser (lines 10 and 11).

PHP file ‘adjust.php’ modifies only the first stereo system and saves the results in ‘stereo_adj.xml’. Load the file in a browser. Figure 5.25 shows the results. Just to be sure, check the contents of ‘stereo_adj.xml’ in the ‘xml_docs’ directory.

 1 <?php

 2 // adjust.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 $reduced = substr($xml->stereo[0]->price, 1);

 5 $reduced = str_replace(",","",$reduced);

 6 $reduced = $reduced * .85;

 7 $xml->stereo[0]->price = '$' . number_format($reduced,2);

 8 $xml->stereo[0]['id'] = 'fw191';

 9 $xml->stereo[0]->speakers = 'Focal';

10 if($xml->asXML('xml_docs/stereo_adj.xml'))

11 { echo 'XML saved'; }

12 else

13 { echo 'Could not save XML'; }

14 ?>

Figure 5.25

Figure 5.25 Display Showing that Modified XML Was Saved

The logic in ‘adjust.php’ is the same as in ‘change_specific.php’, except that the results are saved to ‘stereo_adj.php’ (line 10) and there is no ‘header()’ to display results.

PHP file ‘remove.php’ removes some nodes from all stereo systems. Changes are not permanent since the XML is not saved. Figure 5.26 shows partial results, verifying that the first stereo system no longer has an ‘id’ attribute or nodes ‘cdp’, ‘wire’, or ‘price’.

 1 <?php

 2 // File remove.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach ($xml->stereo as $stereo)

 5 {

 6 unset($stereo['id']);

 7 unset($stereo->cdp);

 8 unset($stereo->wire);

 9 unset($stereo->price);

10 }

11 header ('Content-Type: text/xml');

12 echo $xml->asXML();

13 ?>

Figure 5.26

Figure 5.26 Display Showing Partial Results after Deleted Pieces

The ‘unset()’ function is used to remove ‘id’ (line 6), ‘cdp’ (line 7), ‘wire’ (line 8), and ‘price’ (line 9) from all stereo systems. Nothing is actually changed because results are only displayed (line 11 and 12).

PHP file ‘remove_specific.php’ removes some nodes from the first stereo system. Changes are not permanent since XML is not saved. Figure 5.27 shows the results.

 1 <?php

 2 // File remove_specific.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 unset($xml->stereo[0]['id']);

 5 unset($xml->stereo[0]->cdp);

 6 unset($xml->stereo[0]->wire);

 7 unset($xml->stereo[0]->price);

 8 header ('Content-Type: text/xml');

 9 echo $xml->asXML();

10 ?>

Figure 5.27

Figure 5.27 Display Showing Deleted Pieces from First Element

Lines 4–7 remove ‘id’, ‘cdp’, ‘wire’, and ‘price’ from the first stereo system. No changes are actually made because XML is not saved. Lines 8 and 9 display results.

PHP file ‘remove_save.php’ removes some nodes from all stereo systems and saves the results in ‘stereo_less.xml’. I begin by removing ‘id’, ‘cdp’, ‘wire’, and ‘price’ from all stereo systems (lines 4–10). To preserve proper formatting, I use DOM. First, I create a new ‘DOMDocument’ (line 11). Second, I do not preserve whitespace in the document (line 12). Third, I format output correctly by setting ‘formatOutput’ to ‘true’ (line 13). Fourth, I load the ‘asXML()’ output into the object (line 14) and save to ‘stereo_less.xml’ (line 15). By saving the results to a new file name, I preserve the original data in ‘stereo.xml’.

 1 <?php

 2 // File remove_save.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach ($xml->stereo as $stereo)

 5 {

 6 unset($stereo['id']);

 7 unset($stereo->cdp);

 8 unset($stereo->wire);

 9 unset($stereo->price);

10 }

11 $dom = new DomDocument();

12 $dom->preserveWhiteSpace = false;

13 $dom->formatOutput = true;

14 $dom->loadXML($xml->asXML());

15 $dom->save('xml_docs/stereo_less.xml');

16 ?>

The contents of ‘stereo_less.xml’ should look like the following code.

<?xml version="1.0"?>

<root>

<stereo>

<name>Joyous Sound</name>

<preamp>Spectral</preamp>

<amp>Linn</amp>

<speakers>Opera</speakers>

</stereo>

<stereo>

<name>Nirvana</name>

<preamp>Belles</preamp>

<amp>Belles</amp>

<speakers>Focal</speakers>

</stereo>

<stereo>

<name>Heaven</name>

<preamp>MBL</preamp>

<amp>MBL</amp>

<speakers>MBL</speakers>

</stereo>

<stereo>

<name>Ear Candy</name>

<preamp>AudioPax</preamp>

<amp>Linn</amp>

<speakers>Energy</speakers>

</stereo>

</root>

Add Attributes and Elements to an XML Document

With ‘SimpleXML’, it is relatively easy to add attributes and elements to an XML document. PHP file ‘add_attribute.php’ adds an attribute to an existing element tag and displays the results.

 1 <?php

 2 // File add_attribute.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach ($xml->stereo as $stereo)

 5 {

 6 if(strpos($stereo->speakers,'Opera') !== false)

 7 { $stereo->speakers->addAttribute('type','monitor'); }

 8 else

 9 { $stereo->speakers->addAttribute('type','floor'); }

10 }

11 header ('Content-Type: text/xml');

12 echo $xml->asXML();

13 ?>

Function ‘strpos()’ finds the position of the first occurrence of ‘Opera’ in the element value of each ‘speaker’ (line 6). If the string contains the word ‘Opera’, attribute ‘type’ is assigned ‘monitor’ (line 7). Otherwise, attribute ‘type’ is assigned ‘floor’ (line 9). Results are then displayed (lines 11 and 12). Figure 5.28 shows that attribute ‘monitor’ was added to ‘Opera’ speakers for the first stereo system.

Figure 5.28

Figure 5.28 Display Showing Added Attribute to XML

PHP file ‘save_attribute.php’ contains the same basic logic, but saves changes to ‘stereo_add.xml’. In this case, we need not worry about XML formatting because modifications are inside element tags. That is, no structural changes are made to the XML document. Figure 5.29 shows the results.

 1 <?php

 2 // File save_attribute.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach ($xml->stereo as $stereo)

 5 {

 6 if(strpos($stereo->speakers,'Opera') !== false)

 7 { $stereo->speakers->addAttribute('type','monitor'); }

 8 else

 9 { $stereo->speakers->addAttribute('type','floor'); }

10 }

11 ob_start();

12 if($xml->asXML('xml_docs/stereo_add.xml'))

13 { echo 'XML saved'; }

14 else

15 { echo 'XML not saved'; }

16 ob_flush();

17 ob_end_clean();

18 ?>

Figure 5.29

Figure 5.29 Display Showing that Added Attribute to XML Was Saved

Function ‘ob_start()’ (line 11) turns on output buffering. While output buffering is active, no output is sent from the script. Instead, output is stored in an internal buffer. Line 12 saves the changes to ‘stereo_add.xml’. Function ‘ob_flush()’ (line 16) outputs what is stored in the internal buffer. Function ‘ob_clean()’ (line 17) erases the output buffer. Programming flexibility is increased by being able to control the output buffering process. The contents of ‘stereo_add.xml’ should look like the following code.

<?xml version="1.0"?>

<root>

 <stereo id="ow191">

  <name>Joyous Sound</name>

  <preamp>Spectral</preamp>

  <amp>Linn</amp>

  <cdp>Nagra</cdp>

  <speakers type="monitor">Opera</speakers>

  <wire>Wireworld</wire>

  <wire>JPS Labs</wire>

  <wire>Shunyata</wire>

  <price>$20,000.00</price>

 </stereo>

 <stereo id="ms299">

  <name>Nirvana</name>

  <preamp>Belles</preamp>

  <amp>Belles</amp>

  <cdp>Esoteric</cdp>

  <speakers type="floor">Focal</speakers>

  <wire>Shunyata</wire>

  <price>$10,000.00</price>

 </stereo>

 <stereo id="jj101">

  <name>Heaven</name>

  <preamp>MBL</preamp>

  <amp>MBL</amp>

  <cdp>MBL</cdp>

  <speakers type="floor">MBL</speakers>

  <wire>MIT</wire>

  <wire>Nordost</wire>

  <price>$120,000.00</price>

 </stereo>

 <stereo id="dp530">

  <name>Ear Candy</name>

  <preamp>AudioPax</preamp>

  <amp>Linn</amp>

  <cdp>Linn</cdp>

  <cdp>Esoteric</cdp>

  <speakers type="floor">Energy</speakers>

  <wire>Crystal</wire>

  <wire>JPS Labs</wire>

  <wire>Wireworld</wire>

  <wire>Shunyata</wire>

  <price>$30,000.00</price>

 </stereo>

</root>

PHP file ‘add_elements.php’ adds new child elements to the first stereo system by matching the ‘id’ attribute’ and displays results (without saving). Figure 5.30 shows the added elements to the first stereo system.

 1 <?php

 2 // File add_elements.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach ($xml->stereo as $stereo)

 5 {

 6 if($stereo['id'] == "ow191")

 7 {

 8 $distributor = $stereo->addChild('distributor');

 9 $distributor->addChild('company','Sound Hounds');

10 $distributor->addChild('location','LA, CA');

11 $distributor->addChild('country','USA');

12 }

13 }

14 header ('Content-Type: text/xml');

15 echo $xml->asXML();

16 ?>

Figure 5.30

Figure 5.30 Display Showing Added Elements to XML

Line 6 checks if ‘id’ is ‘ow191’. If so, ‘child’ node ‘distributor’ is added to this element (line 8). Under ‘distributor’, elements ‘company’, ‘location’, and ‘country’ are added, with their respective values (lines 9–11). Results are then displayed to a browser (lines 14 and 15).

PHP file ‘add_save_elements.php’ adds a new ‘child’ node to the element with ‘id’ of ‘ow191’ and saves changes to ‘stereo_add_save.xml’. Figure 5.31 shows the modified first stereo system in ‘stereo_add_save.xml’.

 1 <?php

 2 // File add_save_elements.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 foreach ($xml->stereo as $stereo)

 5 {

 6 if($stereo['id'] == "ow191")

 7 {

 8 $distributor = $stereo->addChild('distributor');

 9 $distributor->addChild('company','Sound Hounds');

10 $distributor->addChild('location','LA, CA');

11 $distributor->addChild('country','USA');

12 }

13 }

14 ob_start();

15 $dom = new DomDocument();

16 $dom->preserveWhiteSpace = false;

17 $dom->formatOutput = true;

18 $dom->loadXML($xml->asXML());

19 $dom->save('xml_docs/stereo_add_save.xml');

20 ob_end_clean();

21 ?>

Figure 5.31

Figure 5.31 Display Showing that Added Elements to XML Were Saved

I start by loading ‘stereo.xml’ into ‘$xml’ as a ‘SimpleXML’ object. I continue by traversing each ‘stereo’ element (lines 4–13). If ‘id’ is ‘ow191’, I add a ‘child’ node ‘distributor’ with its own children (lines 8–11). I continue by starting a new output buffer (line 14), cleaning the output and saving it to ‘stereo_add_save.xml’ with ‘DOMDocument’ (lines 15–19), and clearing the output buffer (line 20).

XPath

XML path language (XPath) is a W3C standard for identifying elements and attributes in an XML document. XPath uses relative paths dependent on the current context, so if the path is already inside a node you can refer to its child node without starting from the root element. XPath can be implemented in PHP on ‘SimpleXML’ objects to run XPath queries on XML data.

XPath Basic Symbols

A leading forward slash ‘/’ indicates the root element and subsequent forward slashes indicate the path from parent to child. A single period ‘.’ represents the current node. Two periods ‘..’ represent the parent node. An asterisk ‘*’ represents any element. Two forward slashes ‘//’ selects all descendants of the current node as well as the current node itself. To identify an attribute, prefix its name with ‘@’.

PHP file ‘xpath_names.php’ extracts and displays all stereo system names from ‘stereo.xml’. This is accomplished by using ‘//name’ to select all descendants. Figure 5.32 shows the name of each stereo system.

1 <?php

2 // File xpath_names.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 $names = $xml->xpath('//name');

5 echo '<ul>';

6 foreach($names as $name)

7 { echo "<li>$name</li>"; }

8 echo '</ul>';

9 ?>

Figure 5.32

Figure 5.32 Display Showing All ‘name’ Elements from XML with XPath

Line 4 extracts ‘name’ elements. The ‘foreach’ loop (lines 6 and 7) traverses the names and displays each ‘name’ value (line 7).

PHP file ‘xpath_attributes.php’ extracts and displays all ‘id’ attributes from ‘stereo.xml’. This is accomplished by using ‘//@id’ to select all descendant attributes. Figure 5.33 shows the results.

1 <?php

2 // File xpath_attributes.php

3 $xml = simplexml_load_file('xml_docs/stereo.xml');

4 $ids = $xml->xpath('//@id');

5 echo '<ul>';

6 foreach($ids as $id)

7 { echo "<li>$id</li>"; }

8 echo '</ul>';

9 ?>

Figure 5.33

Figure 5.33 Display Showing All ‘id’ Attributes from XML with XPath

Line 4 extracts ‘id’ attributes. The ‘foreach’ loop (lines 6 and 7) traverses the ‘id’ attributes and displays each value (line 7).

PHP file ‘xpath_finer.php’ demonstrates how to move through the hierarchy path of the XML document.

 1 <?php

 2 // File xpath_finer.php

 3 $xml = simplexml_load_file('xml_docs/stereo.xml');

 4 $linns = $xml->xpath('//amp[.="Linn"]');

 5 echo '<ul>';

 6 foreach($linns as $linn)

 7 {

 8 $name = $linn->xpath('../name');

 9 $preamp = $linn->xpath('../preamp');

10 $amp = $linn->xpath('../amp');

11 $cdp = $linn->xpath('../cdp');

12 $speakers = $linn->xpath('../speakers');

13 $wire = $linn->xpath('../wire');

14 $price = $linn->xpath('../price');

15 echo "System: " . $name[0] . '<br />';

16 echo "Preamp: " . $preamp[0] . '<br />';

17 echo "Amp: " . $amp[0] . '<br />';

18 echo "CD Player: ";

19 foreach($cdp as $cd)

20 { echo $cd . ' '; }

21 echo "<br />Speakers: " . $speakers[0] . '<br />';

22 echo "Wire: ";

23 foreach($wire as $w)

24 { echo $w . ' '; }

25 echo "<br />Price: " . $price[0] . '<br />';

26 echo '<p>';

27 }

28 ?>

Before I explain the code, let’s review. A single period ‘.’ indicates the current node. To access elements within the selected nodes, use ‘../elementname’. A double period ‘..’ indicates the parent node.

So, I use '//amp[.="Linn"]' (line 4) to select ‘amp’ nodes with the value of ‘Linn’. I use the ‘outer’ ‘foreach’ loop (lines 6–27) to traverses all nodes with ‘Linn’ amps. I use ‘../name’ to access the ‘name’ element for each of these amps (line 8). Lines 9–14 use the same logic to access ‘preamp’, ‘amp’, ‘cdp’, ‘speakers’, ‘wire’, and ‘price’ elements. I display ‘name’, ‘preamp’, and ‘amp’ (lines 15–17). A double period must be used to access these elements because the outer loop is at the ‘<amp>’ node level. Since a stereo system can have more than one ‘cdp’ and ‘wire’, I use ‘foreach’ loops (lines 19 and 20 for ‘cdp’ and lines 23 and 24 for ‘wire’) to display them. Figure 5.34 shows the results.

Figure 5.34

Figure 5.34 Display Showing Data Using XPath

PHP file ‘xpath_implode.php’ extracts and displays all stereo elements and attributes. Since ‘<stereo>’ nodes can have more than one ‘cdp’ and ‘wire’ element, a way must be found to deal with this circumstance. In the previous example, ‘foreach’ loops were used. The ‘implode()’ function is used as a viable alternative to nested loops because it returns a string from the elements of an array. Remember that the XML document is converted to a ‘SimpleXML’ object that stores XML data in arrays. The function accepts a ‘separator’ parameter that specifies what to put between the array elements. A comma is used as the ‘separator’ parameter. Figure 5.35 shows the results.

 1 <?php

 2 // File xpath_implode.php

 3 $font = '<b style=color:red>';

 4 $end = '</b>';

 5 $xml = simplexml_load_file('xml_docs/stereo.xml');

 6 $stereos = $xml->xpath('stereo');

 7 echo "<ul>";

 8 foreach ($stereos as $stereo)

 9 {

10 $id = $stereo->xpath('./@id');

11 $name = $stereo->xpath('./name');

12 $preamp = $stereo->xpath('./preamp');

13 $amp = $stereo->xpath('./amp');

14 $cdp = $stereo->xpath('./cdp');

15 $speakers = $stereo->xpath('./speakers');

16 $wire = $stereo->xpath('./wire');

17 $price = $stereo->xpath('./price');

18 echo '<li>' . $name [0] . ' ';

19 echo '<b style=color:red>'. $id[0] . '</b></li>';

20 echo implode (', ', $cdp) . "<br />";

21 echo implode (', ', $wire) . "<br /><br />";

22 }

23 echo "</ul>";

24 ?>

Figure 5.35

Figure 5.35 Display Showing Data with XPath and Implode

I use a CSS style attribute (line 3) to color code the ‘id’ attributes displayed. I use ‘XPath’ to load all stereo system nodes into ‘$stereos’ (line 6). The ‘foreach’ loop (lines 8–22) traverses the ‘stereo’ nodes. Line 10 loads the ‘id’ attribute into ‘$id’. Lines 11–17 load the ‘name’, ‘preamp’, ‘amp’, ‘cdp’, ‘speakers’, ‘wire’, and ‘price’ element values into their respective variables. Lines 20 and 21 use ‘implode()’ to extract and display ‘cdp’ and ‘wire’ elements easily and elegantly.

Summary

The goal of this chapter was to gain skills in efficiently creating, modifying, saving, and working with XML documents. I used explanations and examples to help you gain the commensurate skills.