Skip to main content

Use PHP to create XForms, Part 2: Using the PHP XForms library to create useful XForms

Tyler Anderson has graduated with a degree in computer science in 2004 and a Master of Science degree in computer engineering in December, 2005, both from Brigham Young University. Tyler is currently a freelance writer and developer for Backstop Media.

Summary:  This two-part article seriess is designed to get PHP developers up to speed in leveraging Web 2.0 XForms forms for their PHP forms development so that they can finally put their outdated Web 1.0 HTML forms away. In Part 1, you created the PHP XForms library. In this article, Part 2, you will enhance the library to include some error checking and convenience functions to help make using the library more manageable, and lastly you'll demo the library by creating a proof of concept XForm.

View more content in this series

Date:  12 Sep 2007
Level:  Intermediate
Activity:  2231 views

The PHP XForms library: What's next?

Get up to speed: Make sure you see Part 1 of this article series, Creating a PHP XForms library.

At this point you have a PHP library that's mostly functional, yet primarily untested. Now it is time to improve the look of the output, validate the inputs to the functions (errors from the XForms preprocessor aren't always as helpful), and see a demonstration of the library in action.

So, let's go ahead and do all of this.


Enhancing the library

With the core of the library complete, you're in good shape to build some enhancements into the PHP XForms library, such as some simple exception throwing and handling, which is useful since not all inputs can acceptably be set, or errors will inevitably result in the resultant XHTML document. In addition, you need a few convenience functions that will help output the XHTML to the Web browser, and another to help format the data with correct indentations. Let's start with error checking.

Error checking

By adding error checking to the library in this section, you'll reduce programming time by having fewer errors to debug through the XForms preprocessor. That will be a big help when all is said and done. You'll perform error checking in two ways:

  • First, for XHTML tags that allow subelements like the final four (action, model, trigger, and repeat) you added in Part 1, you'll make sure that only the allowed XHTML tags are added as children.
  • Lastly, you'll perform error checks on inputs to the individual functions that require it, making sure that certain conditions never occur, and, if they do, an Exception will be thrown, caught, and displayed on the browser.

To begin, go to the list of variables in the xforms_lib.php class file, and add two more variables, as shown in Listing 1.


Listing 1. New variables
                
...
    var $namespaceEvents;
    var $allowed; 
    var $tag;
    
    function xforms_lib($ns, $nsxforms, $nsevents){
... 
        $this->tag = '';
        $this->allowed =
            array('action' => array('dispatch', 'insert',
                                    'setvalue', 'load'),
                  'model' => array('instance', 'submission',
                                   'bind', 'action'),
                  'trigger' => array('label', 'action'),
                  'root' => array('trigger', 'submit', 'select1',
                                  'repeat', 'input', 'output',
                                  'label', 'model'));
...

The new variables are the $allowed and $tag variables. In the constructor you'll initialize them both. You'll learn more about the $tag variable later in this section, but suffice it to say that it holds the name of the tag currently open. Every value in Listing 1 is a top-level XForms XHTML tag. Root is used when none is specified (repeat and root are the same, and so when a repeat tag is open, root is used for this type of error checking). For example, take a look at 'action': Only dispatch, insert, setvalue, and load XHTML tags are allowed as children. Each tag is different, and so a different subset of children are allowed.

Next you'll modify the dispatchTag method to take advantage of this new functionality, as shown in Listing 2.


Listing 2. Modifying the dispatchTag function
                
    function dispatchTag($name, $target){
        $this->check('dispatch');
        $xml = '<xforms:dispatch';
        if($name != '')
            $xml .= ' name="'.$name.'"';
        if($target != '')
            $xml .= ' target="'.$target.'"';
        $xml .= " />";
    }

Notice what's new here. You're passing the name of the tag being created to the new function check (which you'll define in a moment). The rest should be the same.

Now you'll define the check function that performs the actual checks, as shown in Listing 3.


Listing 3. The check function
                
    function check($newTag, $action=''){
        $openTag = $this->tag;
        if($openTag == '')
            $openTag = 'root';

        if($action == 'close' && ($openTag != $newTag || $openTag == 'root'))
            throw new Exception("Cannot close $openTag with a $newTag ".
                                "\"close tag\"statement!");
        else if($action == 'close' && $openTag == $newTag)
            return;
        else if(!in_array($newTag, $this->allowed["$openTag"]))
            throw new Exception("$newTag is not allowed within $openTag!");
    }

  • First if statement: Note that if the empty string ('') is the current value of the $tag class variable, then the $openTag parameter defaults to 'root'.
  • Second if statement: If the opened tag specified in $openTag is being closed, and the opened tag ($openTag) is not the same as the tag ($newTag) being closed (or is the root tag since the root tag can't be closed), then an error will be thrown.
  • First else if statement: Otherwise, if the tag ($newTag) being closed is equal to the open tag ($openTag), then no error results.
  • Second else if statement: However, if the tag being opened/created ($newTag) is not one of the allowed tags being opened within the currently opened tag ($openTag) -- a repeat tag trying to be created within an action tag, for example -- then an error will be thrown. Such errors will be caught and displayed to the user.

You'll now use the check function by adding check statements to the rest of the functions, with a few shown to you as guides in Listing 4 (you can find the rest in the source code download).


Listing 4. Adding check statements
                
    function loadTag($resource = '', $ref = '', $show='replace'){
        $this->check('load');
        $xml = '<xforms:load show="'.$show.'"';
...
    }

    function triggerTagOpen($ref, $submission = '', $label = 'default'){
        $this->check('trigger', 'open');
        $xml = '<xforms:trigger ref="'.$ref.'"';
        if($submission != '')
            $xml .= ' submission="'.$submission.'"';
        $xml .= ' >';
    }

    function triggerTagClose(){
        $this->check('trigger', 'close');
        $xml = '</xforms:trigger>';
    }

Here you need to be sure to add to a call to the $this->check() fucntion to each function that creates an XHTML tag. Note the difference in calling the check function in the triggerTagOpen and triggerTagClose functions where a second parameter is passed ('open' or 'close'), depending on whether or not the tag is being opened or closed.

Now you'll create the function that sets the currently open tag ($this->tag), which is currently being referenced in Listing 3. You'll use this function to set the value of the currently open tag, as shown in Listing 5.


Listing 5. The setTag function
                
    function setTag($t){
        $this->tag = $t;
    }

You'll use this function to set the tag of the highest element. For example, after opening a model tag, you'll call this function as follows: setTag('model'). After closing the tag, you'll no longer be operating under an XForms XHTML tag, and so you'll call it like this: setTag('root'), resetting the currently open tag to root.

Now it's time to validate inputs by throwing exceptions when violations are found. Only five functions require this error-checking capability: submission, bind, load, select1 and instance. Start with submission, as shown in Listing 6.


Listing 6. The submissionTag function: Throwing exceptions on input violations
                
    function submissionTag($id, $action, $method = 'post', $ref='',
                           $instance = '', $replace = ''){
        $this->check('submission');
        if(($instance != '' || $replace != '') &&
           $replace != '' && $instance != '')
            throw new Exception("instance or replace is set, but not both ".
                                "in a submission tag!");
        $xml = '<xforms:submission id="'.$id.'" action="'.$action.
            '" method="'.$method.'"';
...
    }
	

For the submission tag, the $instance or $replace parameters can be set individually, but they cannot both be set at the same time. When using this function an exception will be thrown and displayed to the user.

The idea is similar in the other four functions, as shown in Listing 7.


Listing 7. More functions throwing exceptions on input violations
                

    function bindTag($nodeset, $relevant = '', $calculate = '',
                     $required = ''){
        $this->check('bind');
        if($relevant == '' && $calculate == '' && $required == '')
            throw new Exception("Must set one of: relevant, calculate or ".
                                "required in a bind tag!");
        $xml = '<xforms:bind nodeset="'.$nodeset.'"';
...
    }

    function loadTag($resource = '', $ref = '', $show='replace'){
        $this->check('load');
        if($resource != '' && $ref != '')
            throw new Exception("Cannot specify both a resource and ref ".
                                "for the load tag!");
        else if($resource == '' && $ref == '')
            throw new Exception("Must specify one of: resource or ref ".
                                "for the load tag!");
        $xml = '<xforms:load show="'.$show.'"';
...
    }

    function select1Tag($ref, $label, $itemArray, $itemset){
        $this->check('select1');
        if(is_array($itemArray) && is_array($itemset))
            throw new Exception("Cannot specify both a list of items ".
                                "and an itemset for a select1 element!");
        else if(!is_array($itemArray) && !is_array($itemset))
            throw new Exception("Must specify one of: itemArray or itemset ".
                                "as arrays in the select1 tag!");
        $xml = '<xforms:select1 ref="'.$ref.'">';
        $xml .= '<xforms:label>'.$label.'</xforms:label>';
...
    }

    function instanceTag($id = '', $instanceXML = '', $src = ''){
        $this->check('instance');
        if($instanceXML != '' && $src != '')
            throw new Exception("Must define instance data or a src URL ".
                                "for the instance tag!");
        $xml = '<xforms:instance';
...
    }

Note for the bindTag function that one of the $required, $calculate or $required inputs must be set. For the loadTag function, both the $resource and $ref inputs cannot be set at the same time, but one of the two must be set. The select1Tag function requires that one of $itemArray and $itemset be set but not both. Lastly, the instanceTag function requires that both $instanceXML and $src cannot be set.

This ends the section on error handling. Later in the testing section you'll see how to handle thrown errors and display the error message to the user.

Convenience functions

To top off the error handling you'll add several convenience functions that will assist developers in their XForms development using PHP.

First you'll define a few more class variables, as shown in Listing 8.


Listing 8. More class variables
                
    var $allowed;
    var $print;
    var $indentation;
    var $indentationValue;
    
    function xforms_lib($ns, $nsxforms, $nsevents){
...
        $this->namespaceEvents = $nsevents;
        $this->print = 0;
        $this->indentation = 0;
        $this->indentationValue = "    ";
        $this->allowed =
...
    }

Three more variables are created here to help format the XHTML output. The $print variable specifies whether or not to output XHTML (using echo statements) to the browser directly within class functions using the printData function (defined later). The $indentation variable specifies how many levels of indentation to output when the indentation function is called (defined later), and the $indentationValue variable specifies what to display as indentation (four spaces are used as the $indentationValue here).

Now you'll create a few functions to set the value of $print, and increment or decrement the $indentation count, as shown in Listing 9.


Listing 9. Functions to modify $indentation and $print
                
    function incrementIndentation(){
        $this->indentation++;
    }

    function decrementIndentation(){
        $this->indentation--;
    }

    function setPrint($p){
        if($p)
            $this->print = 1;
        else
            $this->print = 0;
    }

The first two functions either increment or decrement the $indentation class variable initialized to zero in the constructor, and the third one sets $print to 1 if the passed in variable equates to true; otherwise, $print is set to 0.

This next function displays the indentation, shown in Listing 10.


Listing 10. Displaying the indentation
                
    function indentation(){
        $xml = '';
        for($i = $this->indentation; $i > 0; $i--)
            $xml .= $this->indentationValue;
        return $xml;
    }

This function iterates as many times as the value of the $indentation class variable, and for each iteration it adds the $indentationValue class variable to $xml, and returns it. So if $indentation were 2, then eight spaces would accumulate in $xml before being returned. This function, along with the decrementIndentation and incrementIndentation functions, make the XHTML code come out formatted with proper indenting.

Listing 11 shows how to display XHTML using the printData function.


Listing 11. Displaying XHTML
                
    function printData($xml){
        if($this->print){
            echo $this->indentation();
            echo $xml."\r\n";
            return 0;
        }
        return 1;
    }

The end of each function calls the printData function, and based on the value of the $print class variable the XHTML data will either be returned from the function or displayed to the browser. Add it to all tag creation functions, as shown by a few examples in Listing 12. Add the incrementIndentation function to all tagOpen functions and decrementIndentation to all tagClose functions.


Listing 12. Using the printData, incrementIndentation and decrementIndentation functions
                
    function submissionTag($id, $action, $method = 'post', $ref='',
                           $instance = '', $replace = ''){
        $this->check('submission');
...
            $xml .= ' replace="'.$replace.'"';
        $xml .= " />";
        $this->printData($xml);
        return $xml;
    }

    function triggerTagOpen($ref, $submission = '', $label = 'default'){
        $this->check('trigger', 'open');
        $xml = '<xforms:trigger ref="'.$ref.'"';
        if($submission != '')
            $xml .= ' submission="'.$submission.'"';
        $xml .= ' >';
        $this->incrementIndentation();
        $this->printData($xml);
        return $xml;
    }

    function triggerTagClose(){
        $this->check('trigger', 'close');
        $xml = '</xforms:trigger>';
        $this->decrementIndentation();
        $this->printData($xml);
        return $xml;
    }

Here you see the use of $this->printData function in each of the above functions, in Listing 12. Note that even if the data is already displayed to the browser with the call to printData, the XHTML data still gets returned to the statement calling the function. Note also how the triggerTagOpen function uses the incrementIndentation function. Thus, everything after the trigger tag is opened will have extra indentation until the trigger tag is closed with the call to triggerTagClose. This decreases the indentation back to the original amount before the triggerTagOpen function was called.

Add $this->printData($xml); statements to the rest of the functions in the appropriate places (you can use the source code download as a guide). Also add $this->decrementIndentation(); statements to all tagClose statements and $this->incrementIndentation(); statements to all tagOpen statements using Listing 12 and the source code download as guides.

Finally, there are just a few more convenience functions to help make your use of this PHP XForms library a little easier, as shown in Listing 13. You can use these functions in the PHP file to help with the display of necessary regular HTML tags.


Listing 13. Convenience functions
                
    function doctypeTag(){
        $xml = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
        $this->printData($xml);
        return $xml;
    }

    function headTitle($title){
        $xml = "<head><title>$title</title>";
        $this->printData($xml);
        return $xml;
    }

    function closeHeadOpenBody(){
        $xml = "</head><body>";
        $this->printData($xml);
        return $xml;
    }

    function closeBodyCloseHtml(){
        $xml = "</body></html>";
        $this->printData($xml);
        return $xml;
    }

    function outputXHTMLheader(){
        header("Content-Type: application/xhtml+xml; charset=UTF-8");
    }

You'll use each of the above functions in the next section as you formulate the form that you'll create to test the XForms PHP library. They simply create the tags, and output the headers required to be XHTML compliant.


Creating a form using the PHP XForms library

It's time to see in action just how useful this XForms PHP library can be. In this section several XForms widgets will be instantiated and shown to be working using the Firefox XForms plugin.

First take a look at the beginning of the new index page, as shown in Listing 14.


Listing 14: The start of the new index page
                
<?php
include('lib/xforms_lib.php');
$xformsDoc = new xforms_lib("http://www.w3.org/1999/xhtml",
                            "http://www.w3.org/2002/xforms",
                            "http://www.w3.org/2001/xml-events");

$xformsDoc->setPrint(1);

$xformsDoc->outputXHTMLheader();

$xformsDoc->doctypeTag();

$xformsDoc->htmlTag();
$xformsDoc->incrementIndentation();

$xformsDoc->headTitle('XForms served via PHP');

...

?>

The first line includes the library into your PHP file, where you instantiate a new xforms_lib class and pass in the three namespaces. Then you set the $print class variable to 1, specifying to the library to output XHTML directly. The XHTML document is then set up by outputing the appropriate headers, the doctype, the HTML tag, and the title.

Next, you'll see how to create an XForms model using the library (see Listing 15).


Listing 15. Creating an XForms model
                
...

// display model here

$model1 = 'model';
$instance1 = 'instance';
$instance1data = '<root xmlns=""><data1>../here</data1><data2/>'.
    '<data3/><data4/><data5/></root>';
$instance2 = 'instance2';
$instance2data = '<root xmlns=""><data><datum>data1</datum>'.
    '<datum>d2</datum><datum>third data</datum></data>
	             </root>';
$submitButton1 = 'submit1';
$submitButton2 = 'submit2';

$xformsDoc->setTag('root');

$xformsDoc->modelTagOpen($model1);
$xformsDoc->setTag('model');
$xformsDoc->incrementIndentation();

$xformsDoc->instanceTag($instance1, $instance1data);

$xformsDoc->instanceTag($instance2, $instance2data);

$xformsDoc->submissionTag($submitButton1,
                          "receive.php", 'post', "instance('$instance1')");

$xformsDoc->submissionTag($submitButton2,
                          "receive.php", 'post', "instance('$instance2')");

$xformsDoc->bindTag("instance('$instance1')/data4",
                    "instance('$instance1')/data3='1'",
                    "instance('$instance1')/data2");

$xformsDoc->modelTagClose();
$xformsDoc->setTag('root');
$xformsDoc->decrementIndentation();

...

The first several lines are the PHP data variables that contain model, instance, and submission names. Two instance document XML data are also defined. Then the $tag class variable is initialized to 'root' with a call to setTag.

You're now ready to open the model tag, which is what's accomplished on the following line. The $tag variable is then set again to 'model' this time, as the model tag was just opened and root is no longer the top-level tag. This also means that the indentation needs to be incremented, hence the call to incrementIndentation.

Next the two instance documents are instantiated, followed by two submission tags. They will both send one of the two instance documents sent using POST to the receive.php page you created in Part 1. A bind tag is also created that binds data4 (ref) to data2 (calculate) using calculate, and data3 (relevant) makes data4 relevant. Finally, the model tag is closed, $tag gets reset to 'root' and the indentation is decremented.

You can now begin creating the XHTML body, as shown in Listing 16.


Listing 16. Using inputTag and outputTag
                
...

$xformsDoc->closeHeadOpenBody();
$xformsDoc->incrementIndentation();

// display form here

$xformsDoc->inputTag("instance('$instance1')//data1",
                     'data1 (new page input): ');
echo "<br />";
$xformsDoc->inputTag("instance('$instance1')//data2",
                     'data2 (data4 is bound to this field): ');
echo "<br />";
$xformsDoc->inputTag("instance('$instance1')//data3",
                     'data3 (1 makes data4 relevant): ');
echo "<br />data4 (bound to data2): ";
$xformsDoc->outputTag("instance('$instance1')//data4");
echo "<br />data5 (value of select menu shown here): ";
$xformsDoc->outputTag("instance('$instance1')//data5");
echo "<br />";

...

First the XHTML head tag gets closed, and the body tag opened. Then three input tags are created, each referencing to data1, data2 ,and data3, respectively, of $instance1, followed by creating two output tags with each referencing data4 and data5, respectively, also of $instance1.

You can verify in the form that playing with these values will cause the bind element created above in Listing 15 to work as expected.

Next you'll define a select1 tag, as shown in Listing 17.


Listing 17. Defining a select1 tag
                
...

$itemset['nodeset'] = "instance('$instance2')//datum";
$itemset['label'] = '.';
$itemset['value'] = '.';
$xformsDoc->select1Tag("instance('$instance1')//data5", 'select me',
                       '', $itemset);
echo "<br />";

...

First you set up the $itemset array with a nodeset containing all the datum elements in $instance2. The select1 tag is then instantiated and referenced to data5 of $instance1. Note also that playing with the select1 tag will cause the value of the second output tag defined in Listing 16 to be changed accordingly.

Next define the two submit buttons. These two buttons will allow you to see the actual XML of each instance. See Listing 18.


Listing 18. Defining submit buttons
                
...

$xformsDoc->submitTag($submitButton1, 'SubmitTextBoxes');
echo "<br />";

$xformsDoc->submitTag($submitButton2, 'SubmitSelect');
echo "<br />";

...

Labeled appropriately, you can play with the values in the text boxes and select1 menu box, and then click each of the submit buttons to see what the values in your XML are.

Now you'll test out a trigger tag, as shown in Listing 19.


Listing 19. Instantiating a trigger tag
                
...

$xformsDoc->triggerTagOpen("instance('$instance1')//data1", '', 'new page');
echo "<br />";
$xformsDoc->setTag('trigger');

$xformsDoc->actionTagOpen("DOMActivate");
$xformsDoc->setTag('action');

$xformsDoc->loadTag('', "instance('$instance1')/data1",
                    'new');

$xformsDoc->actionTagClose();
$xformsDoc->setTag('trigger');

$xformsDoc->triggerTagClose();
$xformsDoc->setTag('root');

...

Here you create a trigger tag whose label is 'new page.' Once open, you set the $tag variable to 'trigger' and within the trigger tag you create an action tag, activated on the DOMActivate event. Once within this tag you set $tag to 'action,' create a load tag that loads a new page based on the value of data1 (feel free to play with the values of data1 and pressing this trigger). Lastly, you close both the action and trigger tags, and reset $tag to root.

The last tag you test is repeat, as shown in Listing 20.


Listing 20. Creating a repeat
                
...

echo "<br/>repeat data:";
$xformsDoc->repeatTagOpen("instance('$instance2')//datum", 'id1');
$xformsDoc->setTag('root');

$xformsDoc->outputTag(".");

$xformsDoc->setTag('repeat');
$xformsDoc->repeatTagClose();
$xformsDoc->setTag('root');

$xformsDoc->decrementIndentation();
$xformsDoc->decrementIndentation();
$xformsDoc->closeBodyCloseHtml();

?>

Here you specify the contents of the repeat as all the datum children in $instance2. Because repeat is equivalent to root when it comes to adding new elements, you set $tag to 'root.' You then display an output tag that displays the contents of the current XML contents of datum. $tag is then set to 'repeat' before closing the repeat tag, and after closing, $tag is reset to root.

After all the XHTML XForms tags are created, the XHTML document is closed off by decrementing the indentation value twice and closing both the body and html tags.

You can preview the form in Figure 1.


Figure 1. The resulting form
the resulting form

Be sure to right click the XHTML page and click view source, this allows you to verify the XHTML output of your PHP (see Figure 2).


Figure 2. Formatting working
Formatting working

Be sure to play with the form, and with the PHP-generated XForms widgets you created.


Summary

Congratulations. You now have a fairly robust PHP XForms library that you can use and continue to develop and extend. Note that the library and its defined XForms XHTML tags isn't necessarily complete, and there is still room for extensions to this library and its error-checking capabilities. For example, you can define new functions that define new XForms XHTML tags, etc. Essentially, the possiblities are endless and an entire open source project could be centered on an endeaver such as what you've begun to create in this two-part series. Enjoy your journey with PHP and XForms, and good luck!



Download

DescriptionNameSizeDownload method
Part 2 sample codecode_part2.zip4KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

Discuss

About the author

Tyler Anderson has graduated with a degree in computer science in 2004 and a Master of Science degree in computer engineering in December, 2005, both from Brigham Young University. Tyler is currently a freelance writer and developer for Backstop Media.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML
ArticleID=255251
ArticleTitle=Use PHP to create XForms, Part 2: Using the PHP XForms library to create useful XForms
publish-date=09122007
author1-email=tyleranderson5@yahoo.com
author1-email-cc=dwxed@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers