XSD
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
	<xsd:element name="recipes">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element name="recipe" maxOccurs="unbounded">
					<xsd:complexType>
						<xsd:sequence>
							<xsd:element name="authors" minOccurs="1" maxOccurs="1">
								<xsd:complexType>
									<xsd:sequence>
										<xsd:element name="author" type="xsd:string" minOccurs="1" maxOccurs="unbounded"/>
									</xsd:sequence>
								</xsd:complexType>
							</xsd:element>
							<xsd:element name="names" minOccurs="1" maxOccurs="1">
								<xsd:complexType>
									<xsd:sequence>
										<xsd:element name="name" type="xsd:string" minOccurs="1" maxOccurs="unbounded"/>
									</xsd:sequence>
								</xsd:complexType>
							</xsd:element>
							<xsd:element name="ingredients" minOccurs="1" maxOccurs="1">
								<xsd:complexType>
									<xsd:sequence>
										<xsd:element name="ingredientGroup" minOccurs="1" maxOccurs="unbounded">
											<xsd:complexType>
												<xsd:sequence>
													<xsd:element name="ingredient" minOccurs="1" maxOccurs="unbounded">
														<xsd:complexType mixed="true">
															<xsd:attribute name="amount" type="xsd:decimal" use="required"/>
															<xsd:attribute name="units" type="xsd:string" use="required"/>
															<xsd:attribute name="optional" type="xsd:boolean" use="required"/>
														</xsd:complexType>
													</xsd:element>
												</xsd:sequence>
												<xsd:attribute name="group" type="xsd:ID" use="required"/>
											</xsd:complexType>
										</xsd:element>
									</xsd:sequence>
								</xsd:complexType>
							</xsd:element>
							<xsd:element name="directions">
								<xsd:complexType>
									<xsd:sequence>
										<xsd:element name="step" minOccurs="1" maxOccurs="unbounded">
											<xsd:complexType mixed="true">
												<xsd:attribute name="ingredients" type="xsd:IDREF"/>
												<xsd:attribute name="optional" type="xsd:boolean" use="required"/>
											</xsd:complexType>
										</xsd:element>
									</xsd:sequence>
								</xsd:complexType>
							</xsd:element>
						</xsd:sequence>
						<xsd:attribute name="serves" type="xsd:integer" use="required"/>
					</xsd:complexType>
				</xsd:element>
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>
</xsd:schema>
XML
<?xml version="1.0" encoding="UTF-8"?>
<recipes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="recipes.xsd">
	<recipe serves="0">
		<authors>
			<author>Unknown</author>
		</authors>
		<names>
			<name>Pumpkin Bars</name>
		</names>
		<ingredients>
			<ingredientGroup group="ig1">
				<ingredient amount="4" units="none" optional="false">eggs</ingredient>
				<ingredient amount="2" units="cup" optional="false">sugar</ingredient>
				<ingredient amount="16" units="ounce" optional="false">pumpkin</ingredient>
				<ingredient amount="0.75" units="cup" optional="false">melted butter or margarine</ingredient>
			</ingredientGroup>
			<ingredientGroup group="ig2">
				<ingredient amount="2" units="cup" optional="false">flour</ingredient>
				<ingredient amount="2" units="teaspoon" optional="false">baking powder</ingredient>
				<ingredient amount="1" units="teaspoon" optional="false">baking soda</ingredient>
				<ingredient amount="0.5" units="teaspoon" optional="false">cinnamon</ingredient>
				<ingredient amount="1" units="cup" optional="true">chopped nuts</ingredient>
			</ingredientGroup>
			<ingredientGroup group="ig3">
				<ingredient amount="3" units="ounce" optional="false">Cream cheese</ingredient>
				<ingredient amount="6" units="tablespoon" optional="false">Butter or margarine, softened</ingredient>
				<ingredient amount="1" units="teaspoon" optional="false">vanilla</ingredient>
				<ingredient amount="1" units="tablespoon" optional="false">Milk or Half &amp; Half</ingredient>
				<ingredient amount="3" units="cup" optional="false">Powdered Sugar</ingredient>
			</ingredientGroup>
		</ingredients>

		<directions>
			<step optional="false" ingredients="ig1">Combine eggs, sugar, pumpkin, and butter.</step>
			<step optional="false" ingredients="ig2">Add remaining ingredients and stir.</step>
			<step optional="false">Pour into a greased and floured jelly roll pan or if using only half the recipe, into a 9x13 inch pan.</step>
			<step optional="false">Bake 20-25 minutes at 325 degrees.</step>
			<step optional="false" ingredients="ig3">Combine cream cheese, softened butter or margarine, vanilla, milk or half and half, and powdered sugar. Beat together to make frosting.</step>
			<step optional="false">Spread frosting on cooled bars</step>
		</directions>
	</recipe>
	<recipe serves="0">
		<authors>
			<author>Mary Bailey</author>
		</authors>
		<names>
			<name>Beef In A Boat</name>
		</names>
		<ingredients>
			<ingredientGroup group="ig4">
				<!--<ingredient amount="1 &#x2153; cups" optional="false">Sour Cream</ingredient>-->
				<ingredient amount="1.3" units="cup" optional="false">Sour Cream</ingredient>
				<!--<ingredient amount="&#x2154; cups" optional="false">mayonaise</ingredient>-->
				<ingredient amount="0.66" units="cup" optional="false">mayonaise</ingredient>
				<ingredient amount="0.66" units="cup" optional="false">miracle whip</ingredient>
				<ingredient amount="1" units="teaspoon" optional="false">Beau Monde Seasoning</ingredient>
				<ingredient amount="1" units="teaspoon" optional="false">minced onion (in jar)</ingredient>
				<ingredient amount="1" units="tablespoon" optional="false">dried parsley</ingredient>
				<!--<ingredient amount="&#x00BC; teaspoon" optional="false">dry mustard</ingredient>-->
				<ingredient amount="0.25" units="teaspoon" optional="false">dry mustard</ingredient>
				<ingredient amount="1" units="package" optional="false">Dried beef very finely chopped</ingredient>
			</ingredientGroup>
			<ingredientGroup group="ig5">
				<ingredient amount="1" units="loaf" optional="false">Rye Bread</ingredient>
			</ingredientGroup>
			<ingredientGroup group="ig6">
				<ingredient amount="1" units="loaf" optional="true">Rye Bread (for "dippers")</ingredient>
			</ingredientGroup>
		</ingredients>

		<directions>
			<step optional="false" ingredients="ig4">Mix everything together except the rye bread early in the day.</step>
			<step optional="false" ingredients="ig5">Scoop insides out of rye bread and put mixture into rye bread shell just before serving.</step>
			<step optional="true" ingredients="ig6">Cut up additional loaf of bread for "dippers" and arrange them around the outside of the "boat".</step>
		</directions>
	</recipe>
	<recipe serves="4">
		<authors>
			<author>Bob Green</author>
		</authors>
		<names>
			<name>Apple Crisp</name>
		</names>
		<ingredients>
			<ingredientGroup group="ig7">
				<ingredient amount="2" units="cup" optional="false">Apples (peeled, cored, thinly sliced)</ingredient>
				<ingredient amount="0.25" units="cup" optional="false">Apple Juice</ingredient>
				<ingredient amount="1" units="tablespoon" optional="false">Lemon Juice</ingredient>
				<ingredient amount="2" units="teaspoon" optional="false">Corn Starch</ingredient>
				<ingredient amount="1" units="teapsoon" optional="false">Ground Cinnamon</ingredient>
				<ingredient amount="1" units="pinch" optional="false">Nutmeg</ingredient>
				<ingredient amount="2" units="tablespoon" optional="true">Sugar</ingredient>
			</ingredientGroup>
			<ingredientGroup group="ig8">
				<ingredient amount="0.5" units="cup" optional="true">almonds</ingredient>
				<ingredient amount="0.5" units="cup" optional="true">Rolled Oats</ingredient>
				<ingredient amount="0.5" units="cup" optional="false">Brown Sugar</ingredient>
				<ingredient amount="2" units="tablespoon" optional="false">Flour</ingredient>
				<ingredient amount="2" units="tablespoon" optional="false">Butter</ingredient>
			</ingredientGroup>
		</ingredients>

		<directions>
			<step optional="false">Preheat oven to 350F</step>
			<step optional="false">Spray 8 inch pan with cooking spray</step>
			<step optional="false" ingredients="ig7">Combine apples, apple juice, lemon juice, corn starch, ground cinnamon, nutmeg, and (optionally sugar) into pan</step>
			<step optional="false" ingredients="ig8">Combine almonds, rolled oats, brown sugar, flour, and butter.  Sprinkle on top of apples in pan</step>
			<step optional="false">Bake for 25-30 minutes until apples are cooked through, juice is bubbling, and the top is brown.</step>
		</directions>
	</recipe>
</recipes>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
	<html>
		<head>
			<title>Recipes!</title>
			<style>
			div {
				border: 1px dashed black;
				padding: 1em;
				background-color: #FFFFCC;
			}
			ul {
				border: 1px dashed black;
			}
			ol {
				border: 1px dashed black;
			}
			.highlight {
				background-color: yellow;
			}
			</style>
		</head>
		<body>
			<xsl:for-each select="recipes/recipe">
				<div>
				<h2>
					<xsl:for-each select="names/name">
						<xsl:value-of select="." />
					</xsl:for-each>
				</h2>
				<h6>By:&#160;<xsl:for-each select="authors/author">
						<xsl:value-of select="." />
					</xsl:for-each>
				</h6>
				<ul><h5>Ingredients:</h5>
					<xsl:for-each select="ingredients/ingredientGroup/ingredient">
						<li><xsl:attribute name="class"><xsl:value-of select="../@group" /></xsl:attribute>
							<!-- using the unicode space (#160) because &nbsp; doesn't work -->
							<xsl:value-of select="@amount" />
							<xsl:if test="./@units != 'none'">&#160;<xsl:value-of select="@units" /><xsl:if test="./@amount != 1">s</xsl:if></xsl:if>&#160;<xsl:value-of select="." />
							<xsl:if test="@optional='true'">
								&#160;<span style="font-weight: bold; font-style: italic">(optional)</span>
							</xsl:if>
						</li>
					</xsl:for-each>
				</ul>
				<ol><h5>Directions:</h5>
					<xsl:for-each select="directions/step">
						<li><xsl:attribute name="class"><xsl:value-of select="./@ingredients" /></xsl:attribute>
							<xsl:value-of select="." />
							<!-- should setup the optionals to be displayed when user clicks button -->
							<xsl:if test="@optional='true'">
								&#160;<span style="font-weight: bold; font-style: italic">(optional)</span>
							</xsl:if>
						</li>
					</xsl:for-each>
				</ol>
				</div>
				<hr />
			</xsl:for-each>
		</body>
	</html>
</xsl:template>
</xsl:stylesheet>
PHP
<?php
if(isset($_GET['xsl']))
{
	// Load the XML source
	$xml = new DOMDocument;
	$xml->load('recipes.xml');

	$xsl = new DOMDocument;
	//$xsl->load('recipes.xsl');
	$xsl->load($_GET['xsl']);

	// Configure the transformer
	$proc = new XSLTprocessor;

	$proc->importStyleSheet($xsl); // attach the xsl rules
	echo $proc->transformToXML($xml);
}
else
{
	print '<ul>';
	print '<li><a href="' . $_SERVER['PHP_SELF'] . '?xsl=recipes.xsl">My XSL</a></li>';
	print '<li><a href="' . $_SERVER['PHP_SELF'] . '?xsl=recipes-prof.xsl">Prof\'s XSL</a></li>';
	print '</ul>';
	print '<hr />';
	print '<ul id="source">';

	$recipesXSD = implode('', file('recipes.xsd'));
	$recipesXML = implode('', file('recipes.xml'));
	$recipesXSL = implode('', file('recipes.xsl'));
	$recipesPHP = implode('', file('recipes.php'));
	print '<li id="xsdSource"><a href="#xsd">XSD</a></li>';
	print '<li id="xmlSource"><a href="#xml">XML</a></li>';
	print '<li id="xslSource"><a href="#xsl">XSL</a></li>';
	print '<li id="phpSource"><a href="#php">PHP</a></li>';
	print '</ul>';
	print '<hr />';
	print '<div><a name="xsd">XSD</a><pre>' . htmlspecialchars($recipesXSD) . '</pre></div>';
	print '<div><a name="xml">XML</a><pre>' . htmlspecialchars($recipesXML) . '</pre></div>';
	print '<div><a name="xsl">XSL</a><pre>' . htmlspecialchars($recipesXSL) . '</pre></div>';
	print '<div><a name="php">PHP</a><pre>' . htmlspecialchars($recipesPHP) . '</pre></div>';
}
include ('js.php');
?>