I l@ve RuBoard Next Section

• | Table of Contents
Flash and XML: A Developer's Guide
By Dov Jacobson, Jesse Jacobson
Publisher | : Addison Wesley
Pub Date | : November 20, 2001
ISBN | : 0-201-72920-2
Pages | : 368

"This book is an exceptional training manual to bring a web developer or flash designer with scripting skills into an entirely new world of coding."-Christopher Ian Smith, XML Developer, RespondTV.com

"This is a 'must-read' for any Flash developer."-John Paul Rawlins, Editor FlashNewz.com

Flash and XML shows designers and developers how to integrate these powerful technologies and create dynamic web sites. With this thoroughly readable guide you take Flash to the next level, interfacing ActionScript with XML. Empower your Flash projects with dynamic content, backend databases, server-based applications, peer-to-peer, and more.

Flash and XML is a tutorial that brings you up to speed on both technologies, offering clear and concise explanations. In addition, this book presents a number of important web technologies, including PHP, MySQL, and sockets. It shows how to work with these technologies to create n-tier, interactive systems that access the full resources of the Internet.

Sample projects (trivia game, XML browser, simple chat) showcase the capabilities of Flash and XML together and demonstrate important concepts, approaches, and techniques.

I l@ve RuBoard Next Section


I l@ve RuBoard

• | Table of Contents
Flash and XML: A Developer's Guide
By Dov Jacobson, Jesse Jacobson
Publisher | : Addison Wesley
Pub Date | : November 20, 2001
ISBN | : 0-201-72920-2
Pages | : 368

Copyright
Preface
 | What's Up?
 | Don't Stop!
 | Who Are You?
 | Why?
 | Typographic Conventions
Acknowledgments
Chapter 1. Flash Basics
 | The Background of Flash
 | Practical Flash
 | Event-Driven Graphics
 | Building a Button
 | Constructing a Round
 | Making Keyframes
 | Constructing the Responses
 | Finishing Features
 | Conclusion—and Beyond
Chapter 2. Flash ActionScript: Objects and Events
 | Object Orientation Concepts
 | Object-Oriented Theory vs. ActionScript Reality
 | Flash Objects
 | Programmable Button
 | Data Encapsulation
 | Conclusion
Chapter 3. More ActionScript: Data-Driven Interfaces
 | Data-Driven Decisions
 | Data-Driven Everything
 | Isolating the Data
 | Dynamic Creation of Graphics
 | Multiple Questions
 | Conclusion
Chapter 4. XML Fundamentals
 | XML Background
 | How XML Works
 | Designing the Data
 | Conclusion
Chapter 5. XML Structure
 | Element
 | Name
 | Start Tag
 | End Tag
 | Attributes
 | Text (Character Data)
 | Entity References
 | Comments
 | Processor Instructions
 | Conclusion
Chapter 6. XML Validation: DTD
 | Purpose of DTD
 | Valid XML and Well-Formed XML
 | DTD Declarations
 | Entities
 | Namespace
 | Conclusion
Chapter 7. HTTP Connection
 | Characteristics of HTTP
 | Structure of HTTP
 | Loading Data from a File
 | Making a Self-Loading High Score Display
 | Object-Oriented Implementation
 | Data File
 | Flash Download Security
 | Conclusion
Chapter 8. XML Connection
 | Downloading XML
 | XML.load
 | Debugging Flash
 | Event-Driven Functionality
 | Conclusion
Chapter 9. Recursive Approach
 | Event-Driven Code
 | Recursive Design
 | Recursion
 | Recursion in Practice
 | Graphic XML Browser
 | Interactive Node Viewer
 | Conclusion
Chapter 10. XML Server
 | XML Online
 | Dedicated XML Server
 | Middle-Tier Solutions
 | PHP Basics
 | PHP and XML
 | XML-Driven Flash Client
 | Conclusion
Chapter 11. Database Fundamentals
 | Background
 | Advantages of a DBMS
 | Advantages of SQL
 | Keys
 | Conclusion
Chapter 12. SQL Syntax
 | Varieties of SQL
 | Prompt
 | Tables
 | Guidelines for Building a Table
 | Types of Data
 | Adding Data to Tables
 | Reading Records from the Table
 | Conclusion
Chapter 13. Serving from SQL
 | Connecting to a Database
 | Interaction with a Database
 | Parsing Commands
 | Fetching a Row
 | Trial-and-Error Approach
 | Scalable Alternative
 | Conclusion
Chapter 14. XML Upload
 | Flash Client
 | Uploading to PHP
 | Conclusion
Chapter 15. Two-Way XML
 | Event-Driven Parsing
 | Building the PHP Parser
 | Conclusion
Chapter 16. Cookies
 | ActionScript XML Objects
 | Stateless Persistence
 | Conclusion
Chapter 17. Three Tiers
 | Database
 | Flexible Middle Tier
 | Client Tier
 | Conclusion
Chapter 18. Flash to the World
 | Domain Perimeter
 | PHP Proxy
 | Client
 | RSS File
 | RSS Browser
 | Conclusion
Chapter 19. XML Sockets
 | Advanced Socket Programming
 | XMLSocket
 | Simple Socket System
 | Simple Socket Server
 | Conclusion
Chapter 20. Multiplexed XML Sockets
 | Chat
 | Client with History
 | Multiclient Server
 | Beyond PHP
 | Conclusion
Chapter 21. End Note
Appendix A. Microsoft Compatibility
 | ASP Code with Flash
 | IIS and PHP
Appendix B. Scalable Vector Graphics (SVG)
Appendix C. Tools and Sources
 | Network Tool
 | Servers
 | XML Tools
 | PHP Tools
 | MySQL Tools
 | Flash
I l@ve RuBoard


Copyright

Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and Addison-Wesley was aware of a trademark claim, the designations have been printed with initial capital letters or in all capitals.

The authors and publisher have taken care in the preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use of the information or programs contained herein.

The publisher offers discounts on this book when ordered in quantity for special sales. For more information, please contact:

Pearson Education Corporate Sales Division

201 W. 103rd Street

Indianapolis, IN 46290

(800) 428-5331

corpsales@pearsoned.com

Visit AW on the Web: www.aw.com/cseng/

Library of Congress Cataloging-in-Publication Data

Jacobson, Dov.

Flash and XML : a developer's guide / Dov Jacobson and Jesse Jacobson.

p. cm.

Includes index.

ISBN 0-201-72920-2 (alk. paper)

1. XML (Document markup language) 2. Web sites—Design. 3. Flash (Computer file)

I. Jacobson, Jesse. II. Title.

QA 76.76.H94 J334 2002

005.7'2—dc21 2001045738

Copyright © 2002 by Dov Jacobson and Jesse Jacobson

All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. Printed in the United States of America. Published simultaneously in Canada.

For information on obtaining permission for use of material from this work, please submit a written request to:

Pearson Education, Inc.

Rights and Contracts Department

75 Arlington Street, Suite 300

Boston, MA 02116

Fax: (617) 848-7047

Text printed on recycled paper

1 2 3 4 5 6 7 8 9 10—DOC—0504030201

First printing, October 2001

Dedication

To three we love— Shez, Judy, and Anna Leah Jacobson


Preface

Flash can make fabulous introductions to fancy web sites. High-grade wow factor fills the opening screens. Colors are delicious, typefaces are trendy, and movement is exquisite. Rave music is looping and attitude is everywhere. Each intro is a unique work of art. But they all have one thing in common.

Each has a button that says SKIP INTRO.

Rarely is gratuitous glitz so clearly labeled. When budgets tighten, webmasters will not skip shopping cart, or skip catalog. But they may skip intro.


What's Up?

The web is outgrowing its eye-candy phase, and so must Flash. A web site must be pretty to be viable, but it must be highly functional as well.

Flash pros can't get by on gee-whiz animation or cool interfaces. They'd better prepare to do some of the heavy lifting on the working web. Flash screens must interface with dynamic content, with back-end databases, with server-based applications, and even with other live users.

XML provides a path.


Don't Stop!

Once you connect your Flash code to the outside world, your scope is unbounded. And so is the list of things to learn.

In this book we learn a lot. We begin with Flash and XML. We study networking protocols and PHP server scripting. We learn sockets and SQL and a few fancy XML dialects. We go on to achieve competence in many other related technologies and put them together to build working web systems.


Who Are You?

If you are creative and technical, this book is for you. Maybe you have a design background. You learn whatever technology you need to realize your vision. This book offers you skills that open fresh new worlds. Let's hope your imagination can keep up.

Maybe you have computer science training. You're happy with simple gray buttons. Your art is an elegant code design. You are ready to put Flash to work with all the web technologies you already know.

This book was written by authors who approach Flash from both angles. We want it to speak to both engineers and artists, and we struggled (often with each other) to support both perspectives.


Why?

We wrote this book because it wasn't there when we needed it.


Typographic Conventions

We have adhered to several conventions in this book.

Case Conventions

lowerAndUpperCase is used to name variables and functions in ActionScript

UpperAndLowerCase labels frames and names symbols, objects and constructors.

ALL_UPPER_CASE is used in manifest constants, SQL keywords, PHP globals.

Separated_by_underscores is typically used for PHP names.

Typesetting Conventions

italics indicate names we gave variables, functions, elements, instances, and so on.

codefont shows language keywords and expressions.

codefont is also used to set off the text output of any program.

boldface introduces the first use of an important term.

SMALL CAPS are used for pull-down menu options. Slashes show hierarchy.

"quotes" are reserved for string literals.

These rules are breached occasionally in the book. Sometimes this is for historic reasons, sometimes due to ambiguity and sometimes because it just didn't look right.

Dov and Jesse Jacobson

Berkeley Lake, Georgia

dov@bigfun.net

jesse.jacobson@dartmouth.edu

FlashandXML.com


Acknowledgments

For her sure guidance, for her sturdy belief in this book and her faith in its authors: Mary T. O'Brien, Executive Editor at Addison-Wesley.

For their able support: her editorial assistants, Alicia Carey and Mariann Kourafas.

For his imaginative, clearheaded thinking on many aspects of this book, and especially for its handsome looks: Kevin Smith of stateless designs.

For her care in the tricky transformation of bytes into ink and paper: Jacquelyn Doucette.

For their patience and professionalism in the composition process: Stratford Publishing Services.

For his help in unraveling the toughest technical knots and his aggravating habit of improving our work: Nigel Head. He made his points with some very clever code sequences and, well, we included some verbatim.

For their careful, knowledgeable, and lively technical review: Christopher Ian Smith, John Paul Rawlins, Todd Williamsen, Scott Hamlin, Mike Callery, Jennifer Hall, and the accomplished Brendan Hall.

For their rapid responsiveness and for an enjoyable week of debugging Flash patches together: Zlavik Lozben and Eric J. Wittman of Macromedia.

For his clean direction of Flash technology: Peter Santangeli. (We can all thank him.)

For building Macromedia with his bare hands and his big mouth: our old friend Marc Cantor.

For their enthusiasm and their insights: Colin Moock and other FlashForward speakers, Urami and other macromedia.flash posters, and the lively community at FlashKit.com.

For their support—financial and creative—of the real-life version of the game described in this book: webmaster Garth Moore and producer Zoë Burkholder of the ASPCA (animaland.org).

For permission to reprint his haiku: Walter Vereertbrugghen.

And for their critical contribution to this project: the makers of fine caffeinated beverages all over the world.


Chapter 1. Flash Basics

In this chapter, we learn to build simple interface elements and interactive structures. Both the elementary structures we build and the techniques we learn are used and developed further throughout the book.

This chapter is intended for readers who are new to Flash. It brings them up to speed quickly on editing conventions and ActionScript fundamentals. It also presents an overview of the design of data and execution in the trivia game.


The Background of Flash

Flash is an interesting phenomenon. It began with technology intended for pen computing. This technology—which translated strokes into drawings—was commercialized as SmartSketch, a personal computer drawing package. SmartSketch made it easy for artists to create pictures that were defined as geometry, not as a screen of pixels. The inventors of the program, such as, Jonathan Gay, understood the advantages of this technology—vector drawing—over the more familiar technique of pixel painting. Geometric descriptions scale perfectly and they are extremely efficient. It is easy to move these pictures from one computer to another. High-level drawing descriptions also make it easy to organize the elements of a drawing—and to manipulate them mathematically.

Easy manipulation means powerful animation. SmartSketch evolved into CelAnimator—a professional cartooning tool. The cartooning world is fairly small—and was smaller still in the 1990s. This vector animation program might have disappeared like its predecessors except for the emergence of the Internet. Device independence and tiny bandwidth requirements were a great fit for the web—which was starved for animation.

Macromedia was introducing its venerable animation system, Director, to the Internet under the name Shockwave. But pixel-based technology such as Director is inflexible and slow to load. In 1996, Macromedia acquired the product, then called FutureSplash. They changed the name to Flash and merged it into the Shockwave line.

In succeeding years, more interactivity and much more scripting were added to Flash.

After a few awkward attempts to develop a proprietary coding syntax, Flash ActionScript was stabilized by adopting ECMA-262 as the basis of its language definition. ECMA-262 is the international standard for web scripting. (JavaScript is an ECMA-compliant language.)

This decision lowered the learning curve for developers and reduced a lot of confusion and ambiguity. It allowed Flash to develop from a simple animation system to a fairly robust environment for the development of interactive web applications.

A second wise decision also involved the adoption of web standards. Macromedia chose XML to be Flash's data-communication technology. Suddenly Flash developers were able to easily interact with an enormous number of web services. This changed the nature of Flash once more. Instead of a stand-alone web application, a Flash presentation can be integrated into a complex system of web-enabled software. Flash is no longer limited to entertainment. It is now ready to provide high-quality interfaces to all kinds of hard working web applications.


Practical Flash

In this chapter, we do not attempt to conscientiously teach Flash graphics creation. That is the subject of many other books. The chapter is not even an overview of the subject. In fact this chapter is more of an underview. We offer a mouse's-eye view of the construction of a few simple but sophisticated interface elements. We don't suggest that our methods are the best approach to each problem. Multifaceted software like Flash invites creative approaches. The approach we show here, based on much experience in team production of code and graphics, is one good approach. Consider it as you develop your own.

Project Design

The trivia quiz game that we will build throughout this book is complex and sophisticated. It starts, however, with some simple ideas that are recognizable in every later step of development.

Before we start any code, even demonstration code, we need to think a little about the design. In general, a detailed planning phase is always a good investment. But in this case, just a simple layout of hierarchy will suffice.

We organize a Game into a series of Quizzes (Figure 1.1). Each Quiz has several rounds. A Round is the presentation of a Question, the player's response, and the scoring. The Question is presented in Q&A format: a single Q and multiple-choice A's.

Figure 1.1. Object Hierarchy of the Trivia Game

In this chapter we create a single functional Round. We present a Question and a series of Answer options and determine whether the player has chosen the correct one. That's all for now.

Our approach is to develop three states: Start state, Win state, and Lose state (Figure 1.2). The latter two states comprise the Response to the user. The Start state encapsulates the Question presentation.

Figure 1.2. Logic at One Round
    I l@ve RuBoard


Event-Driven Graphics

A great strength of ActionScript is that execution is event driven. Things that happen in the outside world cause things to happen in Flash.

Frame Events

The most common event is that time passes. Of course, time passes rather continuously, but in Flash (as in video or film) time is broken into a stream of regular intervals called frames. In a typical Flash application there are 12 frames per second. Twelve times every second, Flash is interrupted by a frame event (more precisely, a frame start event), which starts a cascade of calculations. Anything that is in the process of moving must determine its new position and prepare to redisplay. This is how animation happens.

Each movie has a timeline. When a frame event occurs, it advances a pointer along the timeline to execute any new script or effect any changes scheduled for this frame.

Mouse Events

Frame events give Flash animation. Mouse events give it interactivity. When a user clicks a button, that is an event. It is in fact a series of events: First the cursor enters the button's hot spot (the Roll Over event). Then the user depresses the button (the Press event). Then the user stops pressing (Release) and moves the cursor away (Roll Out). All four events happen often within a half second, but each has a different meaning in the rapidly stabilizing language of user interface:

Roll Over: | What is this?
Press: | I think I want to perform this action
Release: | Yes, I do! I am committed.
Roll Out: | Oh. Well, now I am bored.

A good designer considers each event and supplies appropriate responses.

Button State

A button has three preprogrammed responses to mouse events. Each is a special named frame.

The Roll Out event causes gotoAndStop("Up").
Roll Over causes gotoAndStop("Over").
The Press event causes gotoAndStop("Down").

Button Event Handlers

There are many more mouse events besides these three. Flash allows us to attach activity to any combination of events.

An event handler connects an event to actions. Event handlers are expressed as code fragments, not as keyframes. They are explicitly packaged as the code called by (for example) the Drag Out event or the <Escape> Key Press event—or both.

There is a fundamental difference between button states and button events. Button state frames are formulated as part of the button object. This is, in effect, a prototype that we construct that lives in the library. Later, we place as many instances of this button in the application as we need.

An instance is not a copy. It is a new reference to the original unchanged prototype. This very powerful concept is at the heart of Flash architecture.

Since the button states are in the prototype, all instances we create (by dragging the button into the scene) share the same states. They behave identically, as long as their behavior is designed in the states.

But every button cannot do the same thing. Some buttons open and some buttons close. Some win and some lose.

To make this happen, Flash allows us to attach event handlers as Object Actions to the instance of the button. They can behave like the button states, and they can do more. They can identify seven mouse events, not just three, and they can attach any ActionScript to those events on that button. Button state frames (in the button's prototype) usually define the button's appearance and its dynamics—how it looks, how it moves, and how it sounds. Event handlers (attached to the instance) usually specify what the button does, how it affects the rest of the world.


Building a Button

An answer option must be sensitive to the mouse. Players click on one of the options and actions are performed. It would also be good to have visible and audible reaction to the various steps in the mouse click and to the mouse rollover as well. Flash gives us all these abilities and more in the Button primitive. To clearly understand how buttons operate, it is best to take a step or two back.

To create a button, invoke Flash's NEW SYMBOL function, tucked away in the Insert pulldown (Figure 1.3).

Figure 1.3. Starting a New Button Symbol

Button Graphics

The button we want is a wide panel of color. One such button will sit behind the text of each answer option and perform all mouse interactions.

We create a filled rectangle— nice and wide and not much taller than a single line of text. It can easily be resized and reshaped later, if necessary. Our example is 400 pixels wide and 30 high.

We also included a simple beveled effect by making an outline rectangle of the same size. For clarity, it is given its own Border layer. New layers are created by clicking the dog-eared icon with a + sign.

Right-clicking on the outline brings us to the Stroke palette, where we set the thickness to 3. We duplicate the Border layer and create Border-top and Border-bottom using cut and paste and rename. We differentiate them by deletion so that Border-top has only the top and left line segments and Border-bottom has the bottom and right segments. The Border-top layer is colored brighter than the panel, while the Border-bottom layer is darker.

Button States

As we've seen, a button has four predefined frames:

  • Up is the default button state.

  • Over starts with mouse roll-on and ends with roll-off or click.

  • Down is from mouse click until release or roll-off.

  • Hit is the invisible shape that defines the click-sensitive area (hot spot) of the button.

We leave Hit as our original rectangle. Its color is meaningless.

For each of the three visible states we create a keyframe (INSERT/KEYFRAME) on each layer. Colorizing these nine keys gives us a lively button. The panel (a solid shape) is colored with the Paint Bucket while the borders (strokes) require the Ink Bottle. Each tool has its own color menu.

Big jumps in color make buttons very interactive but often very unpleasant. Rather than choosing colors from all over the spectrum, we should seek a meaningful palette with subtle transitions.

Button Sounds

Before moving on, let's add a finishing touch to this button: a bit of audible response. Ideally, we would like to hear a sound begin when the button is pressed and conclude when it is released. Like cha-klunk—sounding cha on press, klunk on release. This is possible in Flash, but it is not directly supported. It is much easier to simply attach a sound to the Down frame. Users will hear the sound—the whole sound—as soon as they press the button. Unfortunately, this means that they hear the same confirming cue when they press down but roll off (i.e., change their minds) as when they solidly click and release the button (commit).

Create a Sound layer, which is simply an ordinary new layer that you name Sound (only for our own source clarity, not for Flash). Insert a single keyframe in the Down frame. Choose a sharp sound from the choices in WINDOW/COMMON LIBRARY/SOUNDS. A tiny Play button in the library window allows you to preview each sound. With the keyframe highlighted, merely drag the sound into the main window to integrate it into the button (Figure 1.4).

Figure 1.4. Adding Sound to a Button

By now, you must be ready to try this button out. CONTROL/TEST MOVIE lets you click your new button. Close the window to return to the editor.

Experiment with your own ideas to make the button lively and unique. Subtle tunings make big differences. Get happy with your button and move on.


Constructing a Round

We now build the interactive round. In this case, the round has three possible states:

Start: Present the question and wait for response.

Win: Response was correct.

Lose: Response was incorrect.

Note

We believe that strict discipline is sometimes liberating. Flash allows code to be embedded in any frame and any layer and to be attached to each instance of any object. This much freedom permits the programmer to be creative. Code can be tucked into many corners, and a clever programmer can devise a tricky game of hide and seek.

Hide and seek is fun in the back yard. But it is annoying at the computer and deadly when many people are working together and facing real deadlines.

The challenges of making good software together are numerous enough without inventing more. So we direct our creativity elsewhere and approach code placement with this strict discipline:

If you use ActionScript on any frame, put it in a reserved layer called Code.

Put labels on the top layer— in the Code layer if there is one, or even a Label layer.

Sounds should also be in their own layer(s).

Object actions, such as button actions, are exceptions. If the button is going to do anything, an event handler must be attached to the object instance, not to a keyframe in a timeline layer. Any object action should be extremely simple—it should invoke script visible on this timeline. (This means calling a local function or going to a particular frame.)

Like the button, the Round will establish each of the three states as a separate frame. While Flash frames often represent tiny slices of time in the progress of an animation, they frequently are used instead to represent static screens and as targets in finite-state logic. Often, they are both.

Make a Movie Clip symbol using INSERT/NEW SYMBOL.

Create a Code layer. Like the Sound layer earlier, this is an ordinary layer to which we give a special name. Other than as a reminder for ourselves (and a hint to fellow programmers) layer names have absolutely no meaning.


Making Keyframes

In the Code layer, label three keyframes Start, Win, and Lose. These frames do not have to be consecutive. It is easier to read the labels if they are not and if the LARGE option is chosen behind this tiny icon, found on the right, just above the timeline:

To label a frame easily, open the Frame panel (by right-clicking on a frame, then opening PANELS/FRAME). Type the label name into the obvious place, and voilà!

Construct the Start state

The Start state has a question and four answer options. Each answer option links to either the Lose or Win state. It also has a tiny bit of script.

Double-click on the Start keyframe. This will open an Actions panel.

Type in the single command stop(); and close the panel. This will freeze the movie on the first frame of the scene. It will not animate. It will sit patiently waiting for the user to create the events that bring it to life.

To create the events, the user needs buttons to press.

Button Instance

Make a layer for the first option. Let's call it A0. Highlight a keyframe at the Start frame of A0, and drag in the AnswerButton we already built. The AnswerButton is a symbol in the library. If the library is not already open, select WINDOW/LIBRARY.

The button we see on the Start frame is an instance of the AnswerButton symbol. Many instances can exist for any symbol. They share one copy of the graphics, sound, and embedded code. But each instance has its own set of variables. Each has independent properties. And each instance has unique actions attached to it. That is our next step.

Button Actions

Right-click on the button and open Object Actions (Figure 1.5). Since it is possible to place actions on the keyframe as well as on the button itself, you must take care to double-click on the button, not the frame. The operations look similar but function differently. The title of the ActionScript panel should be "Object Actions."

Figure 1.5. Adding Actions to an Object in Normal Mode

This ActionScript panel can appear in Normal or Expert Mode. In Normal mode you can click on Go To and insert this function. Choose the Type of the goto as Frame Label and select the Lose frame.

The support from the Normal mode training wheels is welcome at first; it helps remind us of options and syntax (was it "Roll Out" or "rollOut"?). But it quickly becomes claustrophobic.

This code will cause the movie to play the Lose frame whenever the mouse release (the final part of a click) occurs with the cursor over this button. This functionality is all we need for now.

Button Text

Now we need to put an actual answer on this option button.

Highlight the keyframe (and defocus the button; Figure 1.6). Using the Text tool, stretch a rectangle across the face of the button and type in the first answer option. Right-click on the text to open the panel Character to choose color, font, and size. Paragraph lets you choose (for example) to center text.

Figure 1.6. Adding Text to a Button

Now we have a single button that is useful for choosing "William the Conqueror" and achieving a Lose result.

Hey, it's a start.

More Buttons

Highlight the keyframe that has the button instance and the text (Figure 1.7). Copy the keyframe. Create three more layers, and paste the keyframe in each. Name them and align the buttons nicely.

Figure 1.7. Quadruplicated Buttons

It is best to align the buttons using numbers in the Info inspector. While Flash features alignment tools to help with layout problems like this, we'd better get used to solving them mathematically. We are ultimately going to abstract and automate these design steps, for example, when programming an unknown number of buttons to space themselves out automatically.

Now it is the work of a few minutes to change the text so that we have four distinct answers. While we are at it, we had better include a question in the Q layer (Figure 1.8).

Figure 1.8. Differentiating the Four Buttons
    I l@ve RuBoard


Constructing the Responses

Now that we have a Question and four possible Answers, we need to formulate a few Responses: Lose and Win.

Lose State

The Lose frame replaces the question on the Q layer with the message "Sorry, wrong" (Figure 1.9). The keyframe has a single line of ActionScript—just

Figure 1.9. The Lose Screen

ActionScript
stop();

This has the desired effect of keeping the player at this screen until he decides to move on. It is a trivial interaction; the player has no other choice and in fact is not penalized for making a bad choice—yet.

The Lose frame drops layers a1, a2, and a3 (three buttons) and changes the text on the remaining button. More important, it also changes the button's Object Actions script. The new Object Action script has a goTo aimed at the Start frame (Figure 1.10).

Figure 1.10. Programming the Try Again Button

Win State

Finally, the Win frame tells the player he won the round and presents a little explanatory text (Figure 1.11).

Figure 1.11. The Win Screen

We have all the parts of a very primitive quiz round. Try it (CONTROL/TEST MOVIE). It moves—but it feels dead. So far there is no scorekeeping, just the display of text. In fact, the player who gets here after three wrong guesses gets pretty much the same payoff as the player who knows the correct answer right away.


Finishing Features

Lets polish this little round with a scoring mechanism and a button that takes the player from the Win screen to the next round. We understand buttons now, so we should be able to create this button without help. Try it!

Keeping Score

Scoring requires a more sophisticated approach to ActionScript than we've seen so far. A score is measured in points. Our first pass is this: When a player answers incorrectly, he loses a point. When he answers correctly, he gains a point. Since he can make several wrong answers before the correct one, we will need a variable, called points.

Many languages require variables to be declared—their name and type (and scope) have to be unambiguously indicated before they can appear in code. Not so in scripting languages like ActionScript. A variable can be written to before it is declared. It can even be accessed before it is declared or written to. In that case, it is of type undefined. Depending on the context, it will provide a value of 0, "", null, false, or NaN (NotANumber).

Although it is somewhat questionable coding practice, we will take advantage of this feature. It keeps our example simple, and it is certainly stable enough for the demonstration.

Open the Actions on the Win keyframe and add these lines:

ActionScript
points++;
trace( points );
stop();

The first line should be familiar: It is the elegant increment operator, introduced by Kernighan and Ritchie and found in many languages. It has the same effect as but it looks infinitely cooler, even after 20 years.

ActionScript
points= points+1;

The second line is a temporary step that prints the value of points in a debugging window of the Flash development environment called Output. It will soon be removed. (In fact, Flash can remove trace() commands automatically when the .swf file is published.)

Open the Lose frame, enter the same code, but replace the increment operator with decrement.

ActionScript
points--;
trace( points );
stop();

Play with it. It works fine, and it is certainly satisfying to know that ignorant players will lose something for choosing the wrong William.

But unless we display the score onscreen, it is pointless.

Displaying the Score with DYNAMIC TEXT

A spot for text has been blocked out on the screen (Figure 1.12) in the upper right. The Character and Paragraph features of the text are similar to those we've applied in the past, but the Text Options are new. Rather than Static Text, Text Options is set to Dynamic Text and displays the points variable. At this point we can remove the trace() statements from the Win and Lose keyframes.

Figure 1.12. Setting Up Dynamic Text

Now play with our round!

With the addition of these features, the game is quite a bit more fun! It is great to keep score (especially when we know the answer). But the most exciting feature is the little button labeled "Next." The "Next" button promises a fresh question. It reminds us that often fun depends on fresh content. This would not be true if we were building an online checkers site, but it is absolutely the case in a trivia game.

Figure 1.13. Win Screen with Score Display
    I l@ve RuBoard


Conclusion—and Beyond

We have created an interesting little quiz round that is clear and responsive and tracks the player's score. But let's be honest—it isn't a very intelligent design. With good questions, this game will be fun to play, but it is never going to be fun to develop, and maintenance will be a tedious feat of endurance. To create the second (or the nth) question will require an effort comparable to the work we just did creating the first.

In the next chapter we make the same user experience a much better experience for those of us who develop the game. Any work that is done creating buttons will be done only once. Software and data-driven elements will leverage the investment we make in the first question and take us one step closer to a clever solution designed around a thin Flash component fed by a rich XML trivia server.


Chapter 2. Flash ActionScript: Objects and Events

This chapter promotes our understanding of Flash to include advanced ActionScripting. We will study data structures in memory and the dynamic creation and destruction of graphic elements. The basics of object-oriented programming are covered.

We start by covering the basics of object orientation. Then we use that knowledge in reforming our quiz. The quiz round we built in the previous chapter is functional but inflexible. We fix that by changing the way it is constructed, from the inside out. The goal is an entirely data-driven trivia game. Eventually the game will get its data from external servers—but not in this chapter. Here we convert our quiz into a data-driven quiz, but the data is hard coded internally in the Flash file itself.

This chapter is intended for those who are still learning Flash ActionScript. It teaches practical issues of execution control, data access, and a quick shower of syntax. It is not meant to be comprehensive coverage of ActionScript, but it will take an attentive novice from a dead stop to functional scripter in a dozen pages.


Object Orientation Concepts

The distinguishing characteristic of object-oriented programming languages is the design of objects.

Suppose we wished to keep track of all the information about the green floppy disk where we are saving this document. We would need many variables. We could give them names like greenDiskFreespace, and greenDiskWriteProtected. But we quickly see that the code grows unwieldy when we wish to also create representations of a translucent red disk (where we store a backup of this chapter), a nontranslucent red disk (which holds a copy of "Ozymandias"), and a yellow disk (which was just formatted).

By contrast, we can create an object. An object is a collection of variables and functions that represent some thing. It can be a physical dingus in the outside world—like a rocketship or floppy disk. It can be a well-understood abstraction—like a savings account or a 3D transformation matrix. Or it can be a new abstraction that we invent for our own convenience—like a trivia question or an internet socket. The fundamental requirement is that our thing has some standardized definition and makeup or that we can create one for it.

To then use the object, we instantiate it. Instantiation consists of creating an actual variable that represents a particular copy of the object we just created—for example, the green instance of the general object type FloppyDisk.

Classes

The code that defines an object needs to be written only once. That abstract definition of the generic object is usually called the class. A class merely explains how the object is constructed, exactly what the object is, and what it does. It is also called a template for the object (or an object's factory). Sometimes the word is prototype or even symbol.

In fact, since the abstract object is called the object, that word is also frequently used to refer to this formal declaration. This is especially true in languages like ActionScript, where the terms class, template and factory do not appear in the language. Class appears briefly in the documentation and symbol is used for the special case of MovieClip objects.

Instances

Once we have designed an object, we want to create a number of instances of the object. After we create a class template for a FloppyDisk object, we might spawn one, two, or even a thousand FloppyDisks. The thousand instances behave the same, but each has its own identity. Each one has a distinct name. The instances of the object FloppyDisk have all its properties and methods, but each has its own individual value. For example, values for its label and its contents would probably be unique. What one instance of FloppyDisk does (for instance, call the function eject()) affects neither the general class nor any other instances of FloppyDisk.

Objects have the following components:

PROPERTIES

An object that simulates a floppy disk has certain qualities, or properties. It may have a physical size, stored in length, width, and thickness, a string that contains its label, a boolean that indicates write protection, and some quantity of data content. By defining what variables every disk has, we can create several disks that have all the variables we just defined. We can either set up the properties to automatically have a default value or assign values to them later.

So far, objects offer little more than other data structures. Why would anyone want to orient their programming around them?

METHODS

Then we add the object functions called methods. Instead of having a loose function called format that can operate on floppy disks, we can have a function called format within the FloppyDisk object. So we can merely say myFloppyDisk.format() and the floppy disk object, which under stands its own inner workings, formats itself. An object function serves two important purposes. First, it allows us to transfer a self-contained object to anyone who may need it. The second purpose is encapsulation.

ENCAPSULATION

Encapsulation hides the inner workings of an object. Whether the FloppyDisk is a software abstraction or a real disk formatting itself with motors and magnets is nobody's business but its own. Without knowledge of the object's insides, other software and programmers are unable to fiddle with the object and break things. Instead, they can manipulate the object only through an interface that the object's creator designates.

Most object-oriented languages (like Java or C++) force programmers to respect these interfaces. ActionScript does not. It is quite easy to cheat and ignore the distinction between public and private parts of the object. But self-discipline will reap important advantages. These advantages all derive from knowing what the object does—but not how it does it.

It is easier to change an object, because we can change the internal functions and variables and leave the outside interface the same. Imagine if we had to go through various modules updating them all because we've figured out how to make the format command more efficient. Encapsulation also makes it easier to work with other people. A single person may need to know exactly how the object works, but a team does not. It doesn't matter to other programmers how it works; it just matters that it does (this is the way we might feel about an air conditioner).

INHERITANCE

Further, we can set up relationships between objects. A FloppyDisk is one specific type (also known as a child) of the general object Disk (other children might include HardDisk and CDROM). In this case, it is said that FloppyDisk inherits from Disk (also known as its parent). For instance, Disk may have a format() command. In that case FloppyDisk would also have a format() command, by default identical to the Disk.format() method. Further, FloppyDisk would have the same variables that Disk has. This is one of the main advantages of object orientation: its inheritance from Object, to Disk, to FloppyDisk (Table 2.1).

Table 2.1. Inheritance from Object, to Disk, to FloppyDisk
OBJECT ToString()
Disk

FloppyDisk
FloppyDisk instance

EXTENSION

Note the extension of the format command in Table 2.1. If a child object did nothing more than duplicate its parent, inheritance would not be a powerful idea. Inherited traits are modified to meet the needs of the child class. An object-oriented software model of a FloppyDisk might format itself exactly as a generic Disk does. (Set data to be nothing, set free space to be maximum.) But the CDROM object would have a different format routine. (Fail.) No problem. It can overwrite the format routine it inherited.

An object can simply extend its inherited methods. For example our FloppyDisk object might add an extra instruction to the Disk.format() it inherits. (Take a really long time.) Rather than copy code, we say, "Take a really long time" and then format yourself however a regular Disk would be formatted. Extension allows changes to be made at the parent level and trickle down. We can reliably make sweeping changes in small amounts of time.


Object-Oriented Theory vs. ActionScript Reality

The implementation of classic object orientation within Flash is far less rigorous than in many other languages, like Java or C++.

ActionScript does not provide the security of meaningful encapsulation. We have seen that an object's private functions and data are not protected from other ActionScript entities. In fact, outside code not only can examine and alter any variable in an object, it also can create new variables and install new functions. Any programmer can easily overwrite the native functions of another programmer's object—at run time. Not only can the functions of an individual instance be overwritten, but they can also be overwritten in the actual class definition. In fact, a programmer can change the class definition of someone else's objects, and those objects that are already in place and working will immediately exhibit the new behavior. This is rather dangerous.

Other features of object-oriented languages are available in Flash. It is possible to create objects. With a bit of work you can create objects that exhibit inheritance. The actual syntax for creating such hierarchical objects bears attention, as it is not documented and it is far from clear. ActionScript objects are never explicitly declared. Rather, they are implied with the following elements:

A constructor is a function that initializes new instances of an object. Every ActionScript function has the potential to be the constructor of objects with the same name. Simply calling the function with the keyword new identifies it as an object constructor. A function named Disk will construct Disk objects when used in the statement.

floppy = new Disk();

Within an object constructor function, the keyword this points to the new object being created. It is commonly used to initiate variables within the new object.

this.formatted = false;

It can also be used to include methods in the object:

this.format = function ( ) ( ...

However, this places an individual copy of the function in each new instance. There is a more efficient way to construct objects. These properties (functions and variables) can be added to the object's prototype. The prototype is a property that all functions have. It is used by objects to identify the functions and variables of which it is composed. The statement

Disk.prototype.format = function ( ) ( ...

establishes a format() function that all Disk objects will automatically have. It is interesting that the function can be installed into the prototype at any time, even after the constructor has been used to make new instances of the object. All existing instances of the object will suddenly have the new function.

If an object instance does not have a function that is being called, the ActionScript engine seeks it in the prototype. It locates the prototype of any object instance by looking at the object's _proto_ variable, which points to the class prototype.

We can use this trick to create a hierarchical object structure, so that we can gain object inheritance.

FloppyDisk.prototype._proto_ = Disk.prototype;

This sets up the Disk object as a parent of the FloppyDisk object. Here is how we would create the hierarchy of Figure 2.1 in ActionScript code.

Figure 2.1. Adding Dynamic Text to the Button

// Defining the Disk Object
function Disk ( ) {
      trace ( "new Disk" );
}
Disk.prototype.label = "unlabelled disk ";
Disk.prototype.directory = function ( ) {
      trace ( "Directory of Disk... .") ;
}
Disk.prototype.format = function () {
      trace ( "Disk formatted.");
}
// Defining the FloppyDisk Object
function FloppyDisk ( ) {
      trace ( "new FloppyDisk" ) ;
}
FloppyDisk.prototype._proto_ =. Disk.prototype;
FloppyDisk.prototype.directory = function ( ) {
      trace ( "Directory of Disk... .") ;
}
FloppyDisk.prototype.format = function () {
      trace ( "Special floppy format tweak");
      this._proto_._proto_.format ();
FloppyDisk.prototype.eject = function () {
      trace ( "Eject floppy disk");
}
// Here we use the function
fd = new FloppyDisk();  // constructor
fd.directory();         // inherited function
fd.format();            // extended function
fd.eject();             // function
trace ( "Label reads:" + fd.label); // class variable
stop();

The resulting output is

new FloppyDisk
Directory of Disk... .
Special floppy format tweak
Disk formatted.
Eject floppy disk
Label reads: unlabelled disk

We see that fd was able to find eject() in its own prototype. fd found the variable label and the function directory() in its parent's prototype. The format() function used code from the prototypes of both FloppyDisk and Disk.


Flash Objects

The trivia game that we will build by the end of this book is complex and sophisticated. It starts, however, with some simple ideas that will be recognizable in each stage of development. Each round of the game is presented in Q&A format: a single Q and multiple choice As. The quizzes are stored in memory as an array of objects.

Predefined Objects

ActionScript comes with several well-defined built-in objects. The XML object will absorb much of our attention. It has methods (like toString()) and properties (like loaded). It also has a constructor, new XML(). This is the method called when the object is created to set up a new instance of the object. A constructor may load default values into variables, position objects, and take arguments that help individualize and define a particular instance. XML, like all provided classes, is also extensible.

Other Flash objects include traditional data types (such as Number, String, Array, and the generic Object) and system constructs (like Date or XMLSocket). Flash also supplies multimedia objects (Color, Sound, Mouse). Finally it includes the object MovieClip, which describes the methods these animations can perform (like play() or gotoAndStop() ) and the properties they all have (like _visible or _xscale).

Code Objects

ActionScript programmers can extend the palette of objects they are given, as in any object- oriented language. The procedure for defining a new object is simple.

To define an object, first a function is written and given the name of the object. This function will be the object's constructor, the new operator that instantiates the object.

ActionScript
function Question( stuff ) {  . . . startup code for Question. . . . }

Then the _prototype property allows us to add methods and properties to our object. It is the object template. The templated definitions

ActionScript
Question._prototype.ask = function () {  . . . method code. . . .}

become specific and real when application code is encountered:

ActionScript
tuffy= new Question( tuffStuff );
tuffy.ask();

MovieClip Objects

Every time we design a MovieClip (the basic unit of Flash Animation), we are essentially making an object. Though they are not always identified as objects, MovieClips have the same qualities as code objects and ought to be considered at the same time. Like objects, they can be prototyped and instantiated, and they have a private name space.

MovieClips have predefined methods and properties. The predefined properties all begin with an underscore: position as _xpos and _ypos, size as _xscale, _yscale, and so on. The methods include play() and stop().

When we create new MovieClips, we frequently give them their own variables and their own functions. These are addressed and used exactly like the built-in properties and methods of MovieClips.

Symbols and Instances

The object definition of a MovieClip is created while it is open in the editing window. What might be called a class or a template elsewhere is here called a symbol. The symbol libraries have plenty of object definitions, both predefined and custom-built. When we place a MovieClip on the stage—inside another MovieClip (or in the Scene)—we have created an instance of the object.

Functions and variables are usually defined when the symbol is created, but they are usually accessed by individual instances: The basic definition of a MovieClip includes scale properties and play methods. But it is an individual instance that is sized or that plays.

We will see the same characteristics in the objects we build—both in our own specialized MovieClips and in our original ActionScript objects.

Note

Instantiation

Sometimes the concept of instantiation can be hard to keep clear. An analogy might be a television program. A master definition is created (at the studio). An infinite number of instances of this show can exist (in living rooms) without diminishing the original. Each instance can be different (larger, smaller, louder, brighter, bluer) than it appeared at the studio. These differences result from adjustments made to the instance (tweaking the volume, for example). Adjustments to the instance do not affect the original definition, nor do they alter other instances.

MovieClip objects behave the same way. You may draw a robot 300 pixels high, but in one scene he might only be 50 pixels high. In fact, he may appear several times in the same scene at different sizes. Each is an independent instance.

ActionScript objects behave the same way—the contsructor and prototype define generic behavior, but the values within their variables create individual identity for the instances.


Programmable Button

We will drive our game with data in objects like those we just studied. But first we must create interface elements that can respond to these objects.

Text on the Button

Our path to a data-driven screen starts at the bottom. Our first goal is to data-drive the text of each answer option button.

The most straightforward approach is to move the text into the actual button. Currently the four lines of answer option text are in the base movie, each superimposed over a button. We want the button to include dynamic text fields, so that the base movie has no text variables—just four buttons and the ability to direct each of them to display the appropriate text. So we add a new layer to the button; for our own convenience, we name it Text. We create a simple line of text in the desired font and location (Figure 2.1). Bearing in mind that this line of text is just a placeholder and that it stands in for hundreds of different lines of text, we carefully size it and position it to make each of the unknown lines look good.

Dynamic Text

Before we put too much effort into making the text pretty, we need to make it smart. This is easily accomplished with the Text Options panel (Figure 2.2).

Figure 2.2. Dynamic Text Based on the Variable text

We convert this text into Dynamic Text to reflect the contents of a variable, not a fixed string set by the designer. The variable name is specified by the programmer. In this case the variable is named (rather boringly) text.

Checking the HTML option enables italics, boldface, and other effects, and it also permits us to rotate the design—by setting a large font size, for example. We use the HTML option in later examples (though certainly nobody can stop you from trying it right now).

Border/Bg is quite useful for quickly creating simple forms, dialogues, and utilitarian interfaces, but is too crude for a design like ours. It creates a very visible and opaque text box for display, input, or both. It is blatantly two-dimensional, and it lacks even a clumsy mechanism for customization.

The Selectable option should be scrupulously disabled for any button face text. If not, mouse clicks on the button face are frequently trapped as text selection rather than as a button press.

Flash does not know the contents of the text that this line will be called on to display. Neither do we, at this point, but we are in a better position to guess. If we can determine that the text requires only a subset of the entire font, we can trim the size of the Flash file. Faster downloads, happier users.

Most designers call for a special font (one that is not installed on every computer). To display this text, the Flash file must include a copy of each font it uses. Flash is smart enough to create a subset of the font, if you are smart enough to know which characters must be in it: include the whole font, or a subset.

The Text Options panel contains punchbuttons for uppercase and lowercase alphabets, digits, punctuation, and a user-supplied list of characters. We indulge a lazy instinct and just click the Include Entire Font Outline icon.

Now let's clear out some of the clutter from the Static Text example.

Highlight all four answer option layers and click on the garbage can symbol:

Separate the question and responses and the (now solitary) answer onto appropriate layers and frames (see Figure 2.3).

Figure 2.3. Separating the Different States

Activating the Dynamic Text

When we run CONTROL/TEST MOVIE, we see nothing very interesting. Try it. The text is still the text we typed in (Figure 2.4). Boring. But remember that the Dynamic Text is pointing to a variable called text that we have yet to define.

Figure 2.4. No Dynamic Text Appears

Open the Actions panel for frame 1 of the main movie (Figure 2.5). Open the keyframe on the Code layer. Above the existing stop instruction, define the variable text by assigning to it a value—in this case an utterly meaningless string literal.

Figure 2.5. Setting the text Variable

text = "This is the good stuff!";
stop();

Now when we test the movie, the button reflects this assignment:

How dynamic is this dynamic text? Pretty dynamic. Try an assignment that is less fixed than the assigned string literal, like

text= new Date().toString();

The results will be different every time it is run. On this lucky afternoon, the button looks like this:

Fancy Text Effects

Smart design can enable pretty design. Clean structure and no duplication create a great environment for creating rich and interesting graphic details.

In the last chapter we made four different lines of text for the answer options. Each one was individually created as a graphic element. We can see that for every round in a quiz, four more text elements will be needed. Creating a small, 10-question quiz would mean editing 40 lines of text, each of which is a complex graphic operation in a drawing editor. Clearly, you would need to keep the graphic ambitions of the text as simple as possible (no special effects). Otherwise the work of maintaining the quiz site would be overwhelming and extremely error-prone.

By contrast, in the new system, using the elements we are now building, all 40 lines of text display the same text graphic. Any work we do is leveraged enormously—4,000 percent on the first quiz! It pays to invest effort into making this line of text as handsome and as responsive as we can.

Drop Shadow

We can create a drop shadow for this line of text, which will give it a dimensional look. We can also animate the color of both text and shadow on each of the three states, just as the color of the button graphic elements change.

Animating the position of text face and shadow will give the impression that the button is tangibly responding to the click of the mouse. Creative readers will certainly explore other interesting ideas. All this goes toward making our button—and the text on it—lively, rich, and real.

Duplicate the text layer. Name the lower one Text-shadow. Make sure Text is bright and Text-shadow is dark. Offset the two layers slightly. Fortunately, the original Static Text contents are still visible, which helps immensely when laying out and animating the drop shadow (Figure 2.6). Each of these two layers (Text and Text-shadow) will have unique keyframes for the Up, Over and Down states.

Figure 2.6. Shadowed Text

Each of the six keyframes can be tweaked and retweaked until the button feels like it lights up magically, pushes in resiliently, or stimulates whatever sensation the user is intended to feel.


Data Encapsulation

If we like the button, we will want to use it for all four answer options. Our instinct is to copy the button for each of four answers (Figure 2.7). This instinct is fine, but immediately we encounter an important problem. Each button is an instance of the same object, and that object is programmed to display the contents of the string variable text. That means that when this movie runs, all four buttons will always sport identical labels (Figure 2.8). The buttons may delight the eye and tickle the mouse finger, but if they offer no actual choice, the players will find this quiz game pretty dull.

Figure 2.7. The Copied Button

Figure 2.8. Four Buttons—One Variable

Consider various strategies for correcting this problem.

  • Return to static text. We probably don't want to do this. Remember that each of our fancy buttons now has six unique text keyframes. Creating a new simple quiz is no longer merely a tedious ordeal of setting 40 graphic text controls. It is now the impossible chore of programming 240 of them.

  • Create a unique variable for each button (text0, text1… textn). A way to escape from the purgatory of cut and paste might be to establish a special variable for each answer option. Creating a new question now requires only a single assignment of each answer option string:

    text0="Snickers";
text1="Mounds";
text2="Runts";
text3="Payday";

    This looks better at first. But implementation of this scheme will introduce nasty compromises into the button design. To link the button to this text, the button code must include the variable name. We are now talking about four different buttons, alike in every way but the name of a variable.

    This is messy. It destroys the sense that the button is an abstracted prototype that is applicable everywhere. And it limits the number of answers that a question can have.

    Perhaps worse is the practical fact that this scheme will freeze development of the button. It seems pretty and bouncy now, but in the future (maybe 10 minutes from now) we might want to tweak it. We would have to replicate each tweak for all four buttons. In real life, we would leave it untweaked.

  • Apply object-oriented data encapsulation. We can eliminate the problems of proliferating edit points by developing a somewhat more complex object structure. This kind of solution will be our choice again and again in this book. The solution is not free: In exchange for eliminating the chore of performing multiple edits (and remembering where they are), we are required to understand more complex structures built of layered abstractions. As designers, we must create these structures with enough architectural clarity that they can be understood by other restless programmers—such as our future selves.

    This task embodies the noble attributes of every programmer: intelligent appetite for abstraction, distaste for hard-coded hacks and lazy aversion to any repetitive task. Our problem is to differentiate the text data used by one button from the data that another button refers to by the same name.

    Buttons themselves cannot encapsulate data. Specifically, they share the namespace with the MovieClip in which they are instantiated. A MovieClip (the base movie or any other) that contains four button instances has only one text variable.

Note

NAMESPACE

Namespace is an important concept in programming. It is the same as scope. Namespace refers to the phenomenon of the same name having different meanings, depending on where it is used. For instance, the identifier "Dad" could refer to billions of people, but there is only one Dad (normally) in any given family. Namespace is similar. Different objects can have properties with the same names. But because each property is accessed within the context of a different object that it resides in, each is different.

The MovieClip itself has a unique namespace. Thus a MovieClip containing four MovieClip instances can have five unique text variables: its own and one for each of the submovies. It does not matter if the four instances are of the same MovieClip symbol (prototype) or not.

So by wrapping our button in a MovieClip, we can make it addressable.

ActionScript
answer0.text="Snickers";
answer1.text="Mounds";
answer2.text="Runts";
answer3.text="Payday";

Though this seems similar to the previous solution, it is very different. There is only one AnswerButton button object and, in fact, only one AnswerOption MovieClip object (see Figure 2.9). Labels like answer0 and answer1 refer to instances of the AnswerOption symbol. AnswerOption contains a single instance of the AnswerButton. The base movie has four instances of AnswerButton, but they are encapsulated in the answer0, answer1, answer2, and answer3 AnswerOptions.

Figure 2.9. Buttons and MovieClip Symbols

Flash does not allow us to name button instances because button instances do not have private namespaces. We can address only the internals of named MovieClip instances. In Flash, the term target is used to refer to a such a named instance.

For historic reasons, Flash has two ways to address targets and their member components. In Flash 4, we struggled to learn a notation that imitates Unix and URL conventions. Object levels are separated by slashes. Variables are separated by colons. Parents are indicated by double dots and the root by a leading slash. In contrast, Flash 5 introduced ECMA style notation (Table 2.2). Everything is separated by dots. Roots and parents have special names. Despite its wordiness and ambiguity (see _root.carrot and _root.carrot in the table), the ECMA notation is undoubtedly superior and is used throughout this book. The ambiguity demonstrated in the table is overcome by the restriction that a MovieClip object and a data variable cannot use the same name in the same namespace.

Table 2.2. Different Syntax for Reference
WHAT WE ARE REFERING TO | SLASH–COLON SYNTAX | DOT SYNTAX
My variable bar | bar | bar
my child foo's variable bar | foo:bar | foo.bar
root variable carrot | /:carrot | _root.carrot
root MovieClip carrot | /carrot | _root.carrot
carrot's submovie tip | /carrot/tip | _root.carrot.tip
my grandfather's dentures | ../../:dentures | _parent._parent.dentures

Now that we have an AnswerOption that is more capable than the AnswerButton, we need to retrofit our Question display. Returning to the Start frame, we can right-click on each of the buttons, one at a time, to automatically select the correct keyframe and bring up the Instance panel.

The leftmost icon is Swap Symbol. Use it to change each of the AnswerButton references to AnswerOptions.

Click the desired choice, and then set the behavior and name it.

Data-Driven Flash

At last we can act like programmers, not clerks. By addressing each instance of the AnswerOption object by name, we can inject data into it (see Figure 2.10).

Figure 2.10. Four Buttons—Four Variables

answer0.text="William the Conqueror";
answer1.text="William the Bastard";
answer2.text="William the Bald";
answer3.text="William the Duke of Normandy";
stop ();

The text, of course, will be visible only as the movie plays.

It is a little disturbing to write to a variable (text) that not only does not yet exist but is in an other object's namespace (answer0.text). Certainly it violates the principles of real object-oriented programming to directly write to a variable that resides in another object. Normally a method of the class would be especially designated to set the text (so that the fundamental design of the object could change, but no code that uses the object would have to). It is shocking simply to write to one that does not even exist. (The AnswerButton will read the text variable but does not declare, define, or assign it.) The primary reason is to prevent accidental overwrites and other miscommunication between programmers, or between us and us when we come back to the code in three weeks.

But this is valid ActionScript. It is a feature of scripting languages like ActionScript and JavaScript (and, as we will see later, PHP) that variables can be established simply by assignment—they need not be declared.

It is also true that ActionScript variables are visible globally. Some other object-oriented languages allow us to restrict access to our object's variables. Data hiding—the protection of an object's variables by the privacy of its namespace—offers a higher degree of security, something like the protection of an unlisted phone number. Although the access syntax and the usage of MovieClip variables are similar to those of the instance variables of an object, this is not pure object-oriented programming. This form of data encapsulation provides for instantiation and packaging of variables for each MovieClip but not data hiding.


Conclusion

We have learned the principles of event-driven design and object-oriented programming. It is clear that they can be used to make a flexible interface. We have begun the first steps to having a data driven quiz. But there is still a lot of work that needs to be done.


Chapter 3. More ActionScript: Data-Driven Interfaces

This chapter completes our brief survey of advanced ActionScripting. We fulfill the goals we developed in the last chapter, focusing on data-driven solutions—design and logic that respond to internal data. This approach includes the dynamic creation and destruction of graphic elements. We build up skills with internal data so that after we learn XML we will easily be able to create presentations driven by external data.

Throughout this book we concentrate on Flash as thin, flexible clients. A thin client presents data that comes from elsewhere; it focuses on the graphic and interactive function, where Flash shines. A flexible client can take a wide variety of inputs and adapt to what it gets; it does not require careful input. There are wins in system maintenance and upgrades, multiplatform support, version control, and download time. But our primary interest in a thin client is to more clearly expose the data inside—to study XML. Our aim is thin, pliant Flash that wraps itself around a fairly visible XML skeleton.

This chapter is less involved with the rules of ActionScript language than with the practical problems of building smart graphics. It demonstrates a useful approach to the problems and is designed to stimulate readers to invent other approaches.


Data-Driven Decisions

So far we have applied labels to buttons, which is nice. The buttons click nicely, too. But nothing happens. By adding functionality to the buttons, we can extend interest in our little game beyond the first few seconds. There are several strategies by which this can be done.

A bit of code might examine the AnswerOption to see if it is the correct one. If that code were in the base movie it might have a local variable where the clicked option leaves its name:

ActionScript
_parent.clicked = _name;        // code in the AnswerOption

Then it might examine the variable to decide whether it is a win or a loss:

ActionScript
if( clicked eq "answer2" ) {     // code in the base movie

This is an adequate but not optimal design. There is a bit of fragility in recording the correct answer as the name of the interface object to which it is attached. For one thing, a change to the order of the questions requires changing the name of the correct choice, which invites error. A second concern is the scattering of information between the child object and its parent.

Better design would involve smarter AnswerOptions. These objects should each know whether they are correct or not. This design also makes it easy to have multiple correct choices or none and even to have choices that are not exactly wrong or right but offer partial credit.

Whether such a feature would be fun is a game design issue, not a technical issue. But it is always good to create systems that do not preclude this kind of flexibility. The effort of realizing the feature is usually made only after interest in it has been demonstrated.

Let us then assume that each object contains not only the text of the answer but also a score variable. For the moment, we will assume that a positive value means a correct answer. But we will start building this with a simple view of the world: Everybody is always wrong.

Clicking the AnswerButton will ultimately cause the base movie to go to the Lose state we built in Chapter 1. Between the button and the base movie is the AnswerOption object, and this is where we want to build the logic.

First we give the object two states: a waiting state and a thinking state. Each is represented by a keyframe. Unlike our experience earlier, these simple states do not require labeled keyframes (though that might have documentation value).

The first frame is the waiting state. Waiting is easy. Just instruct Flash to stop (Figure 3.1). How do we know the waiting is over? A button is clicked. Open the Object (not Frame) Actions panel for the button, and simply have it play when it is clicked. This will get past the stop in struction on the first frame (waiting) and advance to the second frame (thinking; Figure 3.2). In fact, very little thought is required. Since everyone is always wrong (at this stage), the thinking frame merely directs its parent (the base movie) to advance to its Lose state (Figure 3.3).

Figure 3.1. The Waiting State

Figure 3.2. Waiting Ends

Figure 3.3. The Thinking State

ActionScript
_parent.gotoAndPlay("Lose");

Try it out. It is somewhat discouraging because everyone always loses. But otherwise it works. Making it work better is a simple matter of improving the thinking state. If the object looks into itself and examines its own score variable, it will know whether Win or Lose is appropriate:

ActionScript
if( score>0 ){
    _parent.gotoAndPlay ("Win");
      }
else{
    _parent.gotoAndPlay ("Lose");
    }

Of course, this code will not work unless we have set the score variable—at least for the one option that holds the correct answer. The others, being uninitialized variables, have a value of 0. Normally, we would want to assure that there was no score value already there, as would happen if answer 3 was the correct answer to a previous question. In this case, we know that between questions, these buttons will go out of scope. Whenever we are setting the score variable values, all the variables are fresh and valueless. We can do this at the same time that we are applying the labels, in the first frame of the base movie on the Code layer.

ActionScript
answer0.text="William the Conqueror";
answer1.text="William the Bastard";
answer2.text="William the Bald";
answer2.score=1;
answer3.text="William the Duke of Normandy";
stop ();

Now when the movie plays, it plays correctly.


Data-Driven Everything

There are still some missing details. Most important, the answers are dynamic but the questions are not. We correct this by making a question object q with a text variable. This is established in the code on the first frame of the base movie.

ActionScript
q= new Object();
q.text="Which of these names does<br><b>not</b> belong with the others?"
answer0.text="William the Conqueror";
answer1.text="William the Bastard";
answer2.text="William the Bald";
answer2.score=1;
answer3.text="William the Duke of Normandy";
stop ();

The nature of the object q (its text string variable member) is never declared. It is simply constructed when it is invoked. And this works.

Now the hardcoded question in the Start state is replaced with a Dynamic Text field (Figure 3.4). Note the connection to the q.text variable. Note also that HTML is enabled. This will support the HTML tags that we included in the q.text: a line break and the boldface tags around the word "not."

Figure 3.4. The Dynamic Text Field

But the HTML checkbox has an undesirable side effect (at least as of this writing). It causes Flash to ignore the Paragraph settings. The question is no longer centered when it is displayed but rather left justified. To correct this, we can embed HTML alignment tags like

HTML
<p align='center'>

Similarly, we might smarten up the text displayed in the Win state. Attach it to a variable called comment. The content of this variable could be established in the movie's initial frame by a simple straightforward assignment:

ActionScript
comment= "Bald Bill is fake. The other 3 are all the same guy";

But we want to add another degree of flexibility to our system. We want to associate the comments to the answers rather than the question.

So far, we have played with a system that provides only a conclusion, a comment seen by the player only after the round is over, in this case after the correct answer is (finally) chosen. We can easily enable this scenario simply by attaching the comment to the correct answer. And since our structure allows multiple correct answers, each can now have a unique comment.

Even more useful is the new ability to associate comments with wrong answers. Jokes, taunts, and real clues can all make a game more lively. So we add a line into the AnswerOption symbol in the second frame (thinking). We want to simply use the value of the chosen answer to rewrite the comment that will be displayed:

ActionScript
_parent.comment = comment;

This works fine. It updates the comment display with its own local comment. If it has no comment, it updates the variable to be null, and the display is blank. This is the desired behavior.

We can clean up our data by factoring out the HTML centering tags. This is a solid idea since the alignment is related to this particular display far more than to the inherent meaning of the comment. After all, the ideal toward which we are working is display-agnostic content feeding a content-agnostic display. In other words, one part of the program cares solely about the content, and one part cares solely about the display of the content. The content portion will eventually be given to us by XML; the display portion will stick around and eventually become our Flash thin client.

ActionScript
_parent.comment = " <p align='center'>" + comment + "</p>";
if( score>0 ){    _parent.gotoAndPlay ("Win");   }
else         {    _parent.gotoAndPlay ("Lose");  }

To be able to see the hints and jokes, we need to include the comment display in the Lose state as well as the Win. It is a separate keyframe, rather than an extension of the first (like Score), because it needs to be repositioned (see Figure 3.5). Since it represents loss, we give it a slightly different color and point size.

Figure 3.5. Rearranging Keyframes

The organization of layers and keyframes has been cleaned up and we have sneaked in a few sounds. It is still a bit too complex. There is no reason to have separate layers for the question and answers. Layers have no scripting value. They are meaningful only to the editing process: they help us think about, communicate, and especially manipulate the components of a scene. None of this will be made more difficult by collapsing the five layers into a single q & a layer, and the result will be easier to handle.

Now we update the data assignments in the first frame of the movie.

ActionScript
q.text="Which of these 4 names does<br><b>not</b> belong with the others?"
answer0.text="William the Conqueror";
answer1.text="William the Bastard";
answer2.text="William the Bald";
answer2.score=1;
answer2.comment="<b>William the Bastard</b> was the illegitimate son of
       <br>Robert I, Duke of Normandy and a tanner's daughter. Fatherless
       at seven, by fifteen he was acknowledged <b>William the Duke of
       Normandy</b>. At age thirty-eight, he persued his claim to the
       British throne, and at the Battle of Hastings became <b>William the
       Conqueror</b>.<br><br>History records no <b>William the Bald</b>";
answer3.text="William the Duke of Normandy";
answer3.comment="<i>Hint</i>:<br>In 1066, <b>William, the Duke of
       Normandy</b> became<br>William I, the King of England";
stop ();


Isolating the Data

We are well along toward our goal of separating content and presentation. But all the data is still coded directly into the presentation component.

Let's collect the presentation functionality into its own MovieClip symbol. Flash does not make this easy, but with much cutting and pasting of keyframes we can make a symbol called Round. The base movie now has a single instance (called round) of this symbol.

As a first rough step, we can quickly strip all the data out of round and stuff it into a special Round object in the base movie.

ActionScript
r = new Object();
   r.q  = "<p align='center'>Which of these names...
   r.a0 = new Object();
         r.a0.text="William the Conqueror";
   r.a1= new Object();
etc...

You get the idea. We are defining the data structure as we need it. It is an irregular tree that models exactly the data we have on hand. It packages all this data into a single variable r. Once r is filled we can copy into round.

ActionScript
ActionScript
round.r=r;
stop ();

Now the round must be smart enough to get its data from the single object. In this rough first step, we simply copy out of the object the nine pieces of data that we know it contains. We do this one by one (Figure 3.6). Within round the data is distributed into the proper components.

Figure 3.6. Copying Answers Out of a Round

So far we have broken nothing; it still works. And the data is removed from the round proper. But it is still a clumsy system. We can clean it up by creating a regular data structure. Returning to the initial frame of the base movie, we establish an object Question, which is composed of q (the text of the question) and an array of Answers. The Answer object is the text, the score earned, and a comment.

These structures are defined by their constructors:

ActionScript
function Question( questiontext, answerarray ){
    this.q= questiontext;
    this.a= answerarray;
}
function Answer( text, score, comment ){
    this.text=text;
    this.score=score;
    this.comment=comment;

These constructors are invoked (as in similar languages) when the object is first created.

A shortfall compared to similar languages is the lack of multiple signatures. The Answer constructor requires us to supply the text, score, and comment. Classically, we might create another Answer constructor that is distinguished by requiring only text. We would write this constructor to assume the obvious and create a wrong answer option with no comment. This would save coders and readers the tedium of lines filled with formulaic emptiness. But we do not have such a feature, as can be seen by this initialization (in which long lines of text have been truncated for legibility):

ActionScript
new Question ( "Which of these names does<br>
                not belong with the others?",
        new Array(
            new Answer ("William the Conqueror",  0, ""),
            new Answer ("William the Bald", 1, "<b>William the ..."),
            new Answer ("William the Bastard", 0, ""),
            new Answer ("William the Duke of Normandy", 0, "Hint...")
            )
        );
round.r=r;
stop ();

However, we are not forced to do things in such an ugly way. We can work around Flash's lack of multiple signatures. Any parameters we do not pass to our constructor will be of the undefined type, although it is important to note that the missing parameters will be assumed to be the last ones. For instance, if we typed in something like

ActionScript
new Answer ("William the Bastard", "Who would have the guts to call him
             that?");

the constructor would assume that the text was "William the Bastard" and the score was "Who would have the guts to call him that?" It would assume that comment was of the type undefined.

Now that we know that the parameters may be undefined, we can modify our constructor to take different signatures.

ActionScript
function Answer( text, score, comment ) {
   this.text = text;
   if (typeof(score)=="undefined")
      {
      this.score = 0;
      this.comment = "";
      }
   else
      {
      this.score = score;
      if( typeof(comment)=="undefined")
         this.comment = "";
      else
         this.comment = comment;
      }
   }

This code is longer but more robust. There are several ways we could improve it, they are left as exercises for readers to do as much or little of as they see fit. We could check to see whether score was a string or a number (such as 1) when there is no comment. Then, if it were a string, we could presume that comment was intended to be present and the score omitted, and write our code accordingly. We could use the conditional operator to tighten its appearance.

Note

CONDITIONAL OPERATOR

The conditional operator is an operator much like plus or times. Unlike those two, the conditional operator takes in three values. The first value is a boolean and usually is the result of some equality check or comparison such as variable>1. A question mark is placed after the first value. Then a second value is added. This value will be returned by the conditional operator if the first variable evaluates to true. A colon separates the second from the third value, which is returned if the first value equates to be false. This has the same effect as an if/else statement whose sole purpose is to assign one value or another to a variable.

For example,

ActionScript
 IsXOne = (X==1) ? "Yes" : "No";
 IsXTwo = (X==2) ? "Yes" : "No";
 this.score   = (typeof(score)   == "undefined")?  0 : score;
 this.comment = (typeof(comment) == "undefined")? "" : comment;

Regardless of how we end up writing the code, again we have packaged all our information into a single variable r, but now we have been far more rigorous. Though Flash will actually do little to enforce it, we have a well-defined data structure that is both predictable and flexible.

Note that this definition does not demand that our questions have four answer options. As we go further we will work to avoid and eradicate such assumptions. For example, we need to rewrite the step in round where the data object is deconstructed and the presentation screen is created. Round needs to be made aware of the new question structure. An intelligent for loop avoids explicit assumptions about how many answers exist. But the graphics still assume four answers: Round contains four MovieClip instances (answer0, answer1… answer3). These AnswerOption objects are addressed by constructing each name and using eval to treat the string as an object name.

ActionScript
q = r.q;
for( var i=0; i< r.a.length; i++){
  eval("answer"+i).text   = r.a[i].text;
  eval("answer"+i).score  = r.a[i].score;
  eval("answer"+i).comment= r.a[i].comment;
  }
stop ();


Dynamic Creation of Graphics

As data drives the content of this game, we want it also to start driving the game's graphic format. The most rigid feature, at the moment, is the predefined set of four AnswerOptions. The best way to make it more flexible is to spawn AnswerOption instances as they are needed.

Flash has a mechanism for saving prototypes of MovieClips and then instantiating them from within a running application. (The prototypes can even reside on a remote server and be accessed by URL!)

To make a symbol available for instantiation, right-click on its name in the Library panel. The Linkage panel appears. It seems foolish to change the name at this point, but Flash invites you to anyway (Figure 3.7). Resist.

Figure 3.7. Exporting a Symbol

With this symbol available, we can dynamically generate the display by instantiating as many AnswerOptions as the data requires—not always four. The attachMovie method allows us to spawn the new buttons (Figure 3.8).

Figure 3.8. Data-Driven Graphics

The code executes as each round begins. It supplements Flash's automatic creation of graphic elements with our custom code.

ActionScript
for( var i=0; i< r.a.length; i++){
    this.attachMovie("AnswerOption","answer"+i,++_root.maxdepth+5555);
    setProperty( "answer"+i, _y,  50*i);
    eval("answer"+i).text=   r.a[i].text;
    eval("answer"+i).score=  r.a[i].score;
    eval("answer"+i).comment=r.a[i].comment;
    }

  stop();

For each option in the array of answers, we punch out another copy of the AnswerOption object (button, text treatments, sounds, and a little code).

AttachMovie is a method of any MovieClip. Here we have round call its own method and attach the new instances as its own direct children. We supply three arguments:

  • The name of the prototype symbol. This is the name we published from the Library using the Linkage panel.

  • The names of the instances: answer0, answer1, answer2, and so on

  • The depth. We hold our nose and write in this global.

Note

SETTING THE DEPTH LEVEL

This is a weakness in ActionScript. The scripter is expected to know the layout of the object levels at runtime.

Depth is an internal parameter that determines display list priority, among other things. However, like array indices, depth values must be unique. And worse, unlike array indices they must be unique throughout the Flash session, not just within a limited namespace.

No matter how many Flash MovieClips are running, two objects cannot share the same depth. The second object to be assigned will silently annihilate the first.

Yet the programmer cannot read the depth of an object, as he can the other properties. Nor can the programmer inspect system internals through some sort of read-only access functions, perhaps nextAvailableDepth() or isDepthUsed(). Even a desperate programmer willing to experiment is out of luck: the depth of an object cannot be determined at authoring time. It can vary tremendously based on the context of the object's use and the history of any session.

Common practice (and ours) is to pick a large number arbitrarily and assign depths consecutively from that point. Our counter is a global in the root. Any code that uses this counter anywhere increments it, which prevents our depth assignments from colliding. We also add a large constant to avoid the danger that an uninitialized depth counter could cause the destruction of the root or other important objects, which presumably reside at low depths.

But other assignments, from other people's code and from Flash's own operation, can still collide with our levels.

Every error that can happen eventually will. And sooner than we think. The only thing predictable about the resulting bug is that it will be very hard to identify.

As we create the answer instances, we set the _y property (the relative vertical positioning) on each—as a function of i—so they stack up properly. We also plug the appropriate values into the text, score, and comment fields.

In a more sophisticated version, we would hope to find a more thoughtful consideration of r.a.length—the number of answer options for this question. For example, we might program _y as a more complex function of i that squeezes and stretches the spacing. But now we are happy that we can create answers at will and that they look pretty good with two, three, four, or five choices.

But creation is only half the job. We also need to be able to destroy the answers when we are through with them. We have done this casually so far—almost unconsciously. In the static versions of round, all four answer options and the question text are in the layer q&a. This layer has contents only on frame 1 of the movie. When we enter that frame, Flash creates all the graphic elements. When we move out of the frame to Win or Lose, Flash destroys the objects and cleans up (Figure 3.9).

Figure 3.9. Automatic Creation and Destruction

But now that we have created our own graphic structures, they are not located on the timeline. Who will destroy them? We will, of course. It would help if Flash offered an onExit frame event. It doesn't: All frame code implicitly handles the onEnter frame event. We have to split the Start state into two keyframes. The first is entering the state. It creates and displays all the structures and waits for user action. The second keyframe cleans up memory and the screen and decides what to do next. (If this sounds like the waiting and thinking steps of the AnswerOption earlier, it should. As that functionality is assumed by its parent, AnswerOption gets simpler.)

In fact, it becomes very simple (see Figure 3.10). It records its own name as the chosen answer in its parent, the round.

Figure 3.10. Simplified Answer Option

ActionScript
_parent.chosenanswer=_name;

It then tells the round to play, which takes it to the exit frame of its Start state.

ActionScript
_parent.play();

The code on this frame falls into two sections. The first removes the MovieClips from memory. To be easy on the memory manager, they are removed in LIFO order. (Last in, first out sometimes minimizes memory fragmentation.) RemoveMovieClip is a method of instances that have been created dynamically.

ActionScript
for( var i=r.a.length-1; i>=0; i--){
  eval("answer"+i).removeMovieClip();
  }

If we are on this frame, the player has clicked on one of the options, and the option has stored its instance name in our variable chosenAnswer. (This instance name is something like "answer2".) Skip past the six letters in "answer" and convert the rest to a number. This is the option chosen—it is an index into the array of answers.

ActionScript
chosen=Number( chosenanswer.substring( "answer".length));
Figure 3.11. Destroying Graphics

We use it to display the proper comment and to direct the transition to either the Win or the Lose state.

ActionScript
comment="<p align=center>"+r.a[chosen].comment+"</p>";
if( r.a[chosen].score>0 ){
    gotoAndPlay ("Win");
       }
else{
    r.a.splice( chosen, 1);
    gotoAndPlay ("Lose");
    }

Finally, as a nice detail in the case of a wrong answer, we use splice()—a method available to all array objects—to delete this used-up option from the array. When the first frame plays again and round generates its display, it will list one fewer option. The copy of this array in the base movie is unaffected; we are altering only the data in round's private copy.


Multiple Questions

This game is sweet. But market research indicates that a trivia game with only one question can expect a very short shelf life.

We'll fix this. We will build a database of questions and let the player step through them. Later this will be a powerful multitiered trivia server—but not yet. At this point our trivia database is simply an array in memory, generated by ActionScript code. Later, we will externalize it, but first lets get it to work.

All we need to do is create a loop along the timeline of the base movie (Figure 3.12).

Figure 3.12. Step 1: Initializing Data

Frame 1 has the entire database. It includes the definitions of the Question and Answer constructors

ActionScript
function Question( questiontext, answerarray ){

and

ActionScript
function Answer( text, score, comment ){

but now r is not a single Question object but an array of them.

ActionScript
r=  new Array (
        new Question ( "Which of these names do ...
        new Array(
            new Answer ("William the Conqueror",0...
            new Answer ("William the Bald",1,"Will...
            new Answer ("William the Bastard",0,""),
            new Answer ("William the Duke of Norma...
            )
        ),
        new Question ( "Put these in order:",
        new Array(
            new Answer ("micro nano pico gigo", 0,""),
            new Answer ("micro millo nano pico",0,""),
            new Answer ("micro nano pico fempto",1...
            new Answer ("micro pico nano fempto",0...
            new Answer ("milli micro nano giga",0,...
            )
      ),
      new Question ( "<i>Geek question:</i> <br>((i++++...
      new Array(
          new Answer ("True",  0, " <i>Hint</i>: <br>...
          new Answer ("False", 1, "i++ + ++i =<br>i...
          )
       )
    );
whichquestion=0;

It also initializes whichquestion, a variable that indexes into the array of questions to point to the one currently in use.

It is in the second frame that the data is selected from the array and loaded (in a single assignment) into the round (see Figure 3.13).

Figure 3.13. Step 2: Selecting a Question

ActionScript
  round.r=r[whichquestion];
  stop ();

The game waits here for the player (Figure 3.14).

Figure 3.14. The "Next" Button Moves the Round Along

When the player makes it through the round to its Win state, a "next" button appears. This button causes round's parent (the base movie) to play forward along its timeline (Figure 3.15). This third frame of the main timeline completes the game.

Figure 3.15. Step 3: Repeat if There Are More Questions

ActionScript
if( ++whichquestion  < r.length ){   prevFrame ();}

We advance our question pointer. If we have not yet hit the end of the list, we pop back to the second frame, which runs the round again with the fresh question. If we are at the end of the list (whichquestion==r.length), the movie proceeds forward. (In this case we are on the last frame and the Flash player will begin all over again at the first frame.)

Note that it is only on the second frame that the round (or any other graphics) exists. This means that in each round, when the second frame is entered, either from the first frame (first round) or coming backward from the third (all subsequent rounds), Flash instantiates the round object from scratch. After the round is finished and exits the frame, the instance is destroyed. This is a healthy design, where fresh objects are always in use.

We must be cautious not to put any persistent information in the round object. In particular, the points variable was originally in this object. Now that we have multiple questions, we need the points to be valid for multiple rounds. Though we will keep a local round.points for display, the actual score is tracked in the parent object (the base movie, now).

The code at round's Win frame is now

ActionScript
points=++_parent.points;
stop ();

What will the code be in the Lose frame? It differs by two tiny strokes. Instead of adding to the score, we subtract:

ActionScript
points=--_parent.points;
stop ();


Conclusion

We have constructed a solid framework on which to develop the client side of a database-driven Flash application. A trivia quiz gives us a chance to explore many issues of data organization and communication without becoming distracted by seductive animation scenarios. Graphic animation issues have a much greater learning curve and a more tenuous relevance to the multitiered issues we care about.

In these three chapters, we have learned most of the ActionScript concepts we need to create highly functional demonstrations of the material in the rest of the book. Already we have an application that runs briskly, but it cries out almost audibly for an external source of fresh data. That source will be XML.

So we turn our attention away from Flash and begin to unravel XML.


Chapter 4. XML Fundamentals

If you want to connect Flash to the outside world, you need to learn XML. This may strike you as yet another a tedious chore (it did us), but it is not. This is an opportunity to ride a wave that is transforming the Internet. The XML standard is relatively simple, but the XML perspective is profound. Simply learning the syntax gets you going today with Flash—but if you understand the purpose of the standard, you will be ready for the web of tomorrow.

This chapter presents the roots of XML, the philosophy of XML, and the power that the philosophy can provide—especially in a world where change is the only constant and where the role of automation increases every day. These pages can be skipped by any pragmatic programmer. They contain no code or rules of syntax. More thoughtful readers might want to spend fifteen minutes contemplating why XML—which manages to be both simple and clumsy—merits the attention it attracts.


XML Background

Let's begin with a historical perspective.

SGML

Today's World Wide Web is built, of course, on HTML. This method of formatting pages shares ancestry with XML: They both derive from the Standard Generalized Markup Language (SGML). SGML was established around 1969 to unify the Babel of languages then in place to format documents for publication. These "markup languages" evolved out of handwritten typesetting instructions used in the printing industry. And they initially served the same function: to convey formatting decisions from designer to compositor.

Dr. Charles Goldfarb, originator of SGML, foresaw a world in which computer systems as well as humans process documents. SGML can explain to a computer the structure of a very complex text. At first it was used to prepare documents for the printing press. Later it was used to format documents to be read by people on their computer screens. Then Goldfarb and his colleagues became interested in preparing texts that were never to reach human eyes: messages from computer to computer or even from human to computer. Data.

Any SGML object is both document and data. This dual nature increases its value: one aspect emphasizes publishing, the other emphasizes data exchange. As a publishing tool it produces documents of very sophisticated structure and integrity. To data exchange it brings the flexibility of text documents.

Publishing

The publishing model concentrates on documents presented to humans. The markup languages based on SGML have advantages over their predecessors (e.g., HTML over RTF): They allow publishers to create documents that can be viewed in many different systems. By differentiating a heading from the body of text (for example), the standard permits machines with different characteristics to display the same document. The pages don't look the same on all machines, but the basic structure of the document is clearly visible on a graphic workstation or a crude terminal.

The empowering philosophy is abstraction, an expression of the function of each element of text rather than its appearance. The presentation software (browser) is free to decide precisely how to best set off a <BLOCKQUOTE> segment or whether <STRONG> text is best seen in bold or all caps.

Document with Only Formatting:

RTF
{\rtf1\ansi \deff4\deflang1033
{\fonttbl
{\f4\froman\fcharset0\fprq2 Times New Roman;} }
\pard\plain \widctlpar \f4\fs20 \par \pard \qc\widctlpar
{\fs36 Who Needs Titles?\par }
\pard \widctlpar \par \pard \fi720\widctlpar While the body of a document
can be analyzed-by word counting or by sophisticated lexical heuristics-it
is still difficult to automatically extract the author\rquote s original
intent. It is more difficult still to generate a brief text fragment that
telegraphs this meaning. On the other hand, by composing a title, the
author has done exactly this.
\par \pard \fi720\widctlpar \par }

Similar Document with Structure:

HTML
<HTML>
<HEAD>
<TITLE> Who Needs Titles?</TITLE></HEAD>
<BODY>
<P>Software can attempt to analyze a text, but it is very difficult
to automatically extract the author's original intent. It is more difficult
still to generate a brief text fragment that conveys this meaning. On the
other hand, by composing a title, the author has done exactly this.</P>
</BODY>
</HTML>

SGML is very powerful. You can express almost any document in SGML. Unfortunately, nobody could view your document. SGML is so abstract and so complex that no software can reliably display it.

HTML

Enter HTML. In the early 1990s HTML was created as a simplification of the SGML standard. HTML was a slim SGML intended for use in publishing academic and scientific journals in the real world. Its page markup capabilities were extremely simplistic, and its current multimedia capabilities did not exist. HTML had two great strengths. Its simplicity was one. Because it demanded little of the device it was viewed on or of the software required to make the presentation, HTML browsers appeared quickly on many kinds of machines.

Its other great strength is the hyperlink. The Internet created a single global computer system. The hyperlink created a single global document—the World Wide Web. Hyperlinks and the Internet combined to make HTML an enormous success. Publishing became immediate: simple HTML documents could be displayed meaningfully on almost any device anywhere. In the brief childhood of the web, interoperability and intelligence were more important than graphic design.

The success of HTML has made the web into a mass medium. With its success, HTML has faced pressures that have separated it from its original ideals. There is a new perspective. For better or worse, attractive graphics, not platform independence, not metastructure, drives the web. We want attractive screens. Who cares about the document structure?

Actually the structure of information is more important than ever. The potential data content of the web is increasing enormously—perhaps even faster than the growth in visual sophistication. But while we have done a great job of making the pages prettier, we have not done so well at the hard work of making them smart.

The data content of the web is only potential, because most of its organization is lost in descriptions of presentation. We have HTML descriptions like the one below, which is a fragment of the options list for a new Saturn (from edmunds.com). The table may delight human eyes, but it ignores the real needs of a shopping robot or an affiliate marketer or a car buyer with a Palm. Consider, for example, that the "UP0" option package is simply the "UL0" package plus a CD player. A human can deduce this from the information scattered in the table. But it would take very complex and unreliable software to figure it out. Dr. Goldfarb's vision that a document is a data structure has been lost.

HTML
<TABLE BORDER="0" cellPadding="0" cellSpacing="5" WIDTH="100%">
   <TBODY>
     <TR>
       <TD vAlign="top"><FONT FACE="Arial"><I><B>Option<BR>Code</B></I>
         </FONT></TD>
       <TD vAlign="top"><FONT FACE="Arial"><I><B>Option Name</B></I>
         </FONT></TD>
       <TD ALIGN="right" vAlign="top" WIDTH="250"><FONT FACE="Arial"><I>
         <B>Invoice</B></I></FONT></TD>
       <TD ALIGN="right" vAlign="top" WIDTH="65"><FONT FACE="Arial"><I>
         <B>MSRP</B></I></FONT></TD>
     </TR>
     <TR>
       <TD vAlign="top"><FONT FACE="Arial" SIZE="2"><B>UL0</B></FONT>
       </TD>
       <TD vAlign="top"><FONT FACE="Arial" SIZE="2"><B>Radio: AM/FM Stereo
        Cassette and Automatic Tone Control (SC)</BBR>
        Includes theft protection and 4 coaxial speakers. NOT AVAILABLE with
        UP0.</FONT></TD>
       <TD ALIGN="right" vAlign="top" WIDTH="250"><FONT FACE="Arial"
         SIZE="2">$90</FONT></TD>
       <TD ALIGN="right" vAlign="top" WIDTH="65"><FONT FACE="Arial"
         SIZE="2">$100</FONT></TD>
     </TR>
     <TR>
       <TD vAlign="top"><FONT FACE="Arial" SIZE="2"><B>UP0</B></FONT>
       </TD>
       <TD vAlign="top"><FONT FACE="Arial" SIZE="2"><B>Radio: AM/FM Stereo
         CD and Cassette (SC)</BBR>
        Includes auto tone control, theft protection and 4 coaxial
         speakers. NOT AVAILABLE with UL0.</FONT></TD>
       <TD ALIGN="right" vAlign="top" WIDTH="250"><FONT FACE="Arial"
         SIZE="2">$198</FONT></TD>
       <TD ALIGN="right" vAlign="top" WIDTH="65"><FONT FACE="Arial"
         SIZE="2">$220</FONT></TD>
    </TR>
   </TBODY>
 </TABLE>

The result of this HTML is this chart.

OPTIONAL EQUIPMENT (Descriptions & Prices)
CODE | OPTION NAME | INVOICE | MSRP
C60 | Air Conditioning (SC1/SL) Includes CFC-free refrigerant. | $864 | $960
CF5 | Power Sunroof (SL2/SL1/SC) | $652 | $725
HL1 | SC1 Option Package (SC1) Includes air conditioning, cruise control, power exterior mirrors, power windows with driver express-down, power door locks, remote keyless entry and Saturn security system. | $1647 | $1830
JM4 | Anti-Lock Braking System (All Except SL) Includes traction control. | $446 | $495
UL0 | Radio: AM/FM Stereo Cassette and Automatic Tone Control (SC) Includes theft protection and 4 coaxial speakers. NOT AVAILABLE with UP0. | $90 | $100
UP0 | Radio: AM/FM Stereo CD and Cassette (SC) Includes auto tone control, theft protection and 4 coaxial speakers. NOT AVAILABLE with UL0. | $198 | $220

XML to the Rescue

XML can represent this data in a variety of ways. The most natural formulations make clear the items included in each option package and can easily express a superset relationship like that of the UL0 and UP0 packages.

XML
<OPTION
 code= "UP0"
 msrp="198"
 invoice="220"
 availability="SC">
      Radio: AM/FM Stereo CD and Cassette
      <ITEM>CD player</ITEM>
      <OPTION
             code= "UL0"
             msrp="90"
             invoice="100"
              availability="SC">
              Radio: AM/FM Stereo Cassette and Automatic Tone Control
              <ITEM>theft protection</ITEM>
              <ITEM>4 coaxial speakers</ITEM>
              <ITEM>AM/FM radio</ITEM>
              <ITEM>cassette player</ITEM>
       </OPTION>
</OPTION>

Data Exchange

Conversely, a data structure is also a document. Consider a few:

Library catalog entry

Telephone bill

Class schedule

Each of these is a data object with functional work to do. But people need to read them, as well. They exist in database format, and programmers can (and do) make them visible by writing middleware software to convert the content to HTML. But if these objects were in XML format, they could be read by browsers as easily (and sometimes more easily) as a web page and as attractively—if styling files exist.

This is the other approach to Dr. Goldfarb's ideal. The data is self-describing and open. The distinction between document and datagram disappears.


How XML Works

XML provides a very general method for expressing data structure and content. To maintain portability, it consists solely of a stream of text that avoids any assumptions about the systems exchanging information. It is so general and so adaptable that the first thing it declares is which alphabet it is using and how that alphabet is encoded.

XML itself can structure data, but it says nothing about the meaning of the data. XML merely provides a notation in which the meaning can be unambiguously expressed. XML cares solely about syntax; the semantics are up to us.

However, XML provides a technique by which protocols about data elements can be shared. An optional piece of information within an XML file is a reference to an external data definition. Industry associations, academic conferences, and less formal groups can shape XML vocabularies around their needs and store the shape in such an external file. Schoolteachers and astronomers and chemical salesmen and librarians are all forming agreements on ways to express the information they care about. Your own application might have special data requirements, but by using the referencing system that XML provides, you will be able to clearly expose this data to man and machine.

XML Structure

An XML document is a tree of elements. A single element is at the root and encapsulates the entire document. Within the element are attributes that describe it, its plain text content, and its child elements. Each of these is optional. It could contain nothing at all, just exist as a named presence. Within each of its child elements are exactly the same data options. And so on to the child elements of the child elements.

XML Language

The actual look of XML is familiar to anyone who has used its cousin, HTML. Each element of data is blocked out by tags. A start tag <TAG> introduces the element and an end tag </TAG> marks its end. Between them lies the arbitrary content of the element:

XML
<author>Sir Arthur Conan Doyle</author>

Simplicity is its strength. XML by itself can structure data, but it says nothing about the meaning of the data. For example, the XML line

XML
<title>The Hound of the Baskervilles</title>

looks like this line of HTML:

HTML
<title>The Hound of the Baskervilles</title>

This is because the two lines are identical. (Thanks to common SGML roots.) However, in HTML the words have a specific meaning: a title element has certain properties: it is unique to a page, it synopsizes the page, it is used by browsers and by search engines, and so on. In XML, a title is only another element. XML by itself knows no more about a title than it does about a tittle.

XML Vocabularies

Users create vocabularies in XML. Vocabularies can be formalized and published both for human eyes and for XML processors, or they can be implicit in the file. The explicit, public vocabularies will be encountered in Chapter 6 on DTD. Here, we assume that by some external form of agreement, the meaning of element tags is understood by the Flash application and by the source of the XML.

Obviously, without an external vocabulary, XML can provide no validation of the content of an element. When a vocabulary is formalized in a DTD, we can use that file to validate the integrity of our XML and its adherence to the published rules of the vocabulary. We should note that even with a formal DTD vocabulary, we cannot check type on element contents. All the character data between the start and end tags is only strings of text to the XML parser. It makes no attempt to interpret the text.

None of these are numbers:

XML
<pagecount>233</pagecount>
<copyright>1901</copyright>
<isbn>0-7897-2242-9</isbn>

They are simply lines of text. In fact, every XML element can contain only text—and other elements.

Note

WELL-FORMED VS. VALID XML

XML documents that obey the rules of XML syntax (details in the next chapter) are referred to as well-formed. A well-formed document that references an external DTD (or, coming soon, Schema) and conforms to the data design expressed within it can be called valid.

A parser that reads both the XML file and its DTD (or Schema) and tests the former against the latter is a validating parser. Flash does not have a validating parser. This is not uncommon for a presentation-level system. After all, if the file is not valid by the time it is seen in a Flash-enabled browser, it is too late to fix. Validation is more commonly a step in the creation and maintenance of XML systems than in their display.

Unfortunately, some conveniences—such as the expansion of symbols and the assignment of default values—cannot be performed without an external design, so a nonvalidating parser also omits these functions.


Designing the Data

We stand exactly at the intersection of these two historic forces: a pretty web and a smart web. Lucky us—it is a good place to be. Flash is designed to make attractive graphics. Server software was built to do data processing. We must link them.

As you design the data object, you decide the nature of the project, now and in the future. Your creative decisions will make the difference between a flexible, useful, lively site and a disposable web presence that is quickly outdated and must be recreated.

Consider the following issues as you design your data object.

Many Browsers

Your XML should describe the content in a way that is useful on many platforms. Your ActionScript software will interpret the content to create an animated Flash display. But most publishers want to have alternate access for those who won't or can't install the required plugin. So you or someone else may create a script (perhaps an XSL file) that transforms the XML data into HTML, WML, or some other format. Even if this is not yet planned, consider now what information the transformation will require. Clearly you will want to avoid presentation information that limits the platform, for instance, expressing some values in terms meaningful only in ActionScript.

Some platform-dependent data may be inevitable, despite your best efforts to abstract it. As in the following example, you should supply alternative data for other platforms.

Example

You have been asked to create a jukebox application for a music site. The songs exist as music videos in SWF format. XML is used to create a catalogue of music and artists and it locates each SWF music video. If your XML format includes a URL for an MP3 file as well as the SWF file, you will be creating the backbone for an alternate no-Flash jukebox. The people responsible for this site will have to maintain only a single set of XML files to support both the Flash jukebox and the HTML/MP3 version.

It isn't trivial: As of this writing, for example, some mobile devices can display simple HTML and can play MP3s. But there is no Flash plugin available. You may not care about this audience—they aren't Flash folk. But your client probably does.

Many Users

The objects you manipulate will exist in many states. The display the public sees is one view of your data. Since it is how your organization markets its product, it rightfully is considered extremely important. But by considering the other people who will touch this data, a designer can create a sturdy and useful object that documents its own life cycle.

Example

Your client wants to sell college-logo sportswear. Your job is to build the Flash-based storefront. When designing the XML object that represents each entry in the catalogue, you think in terms of the display you want to present to the customer: a photograph of the sweatshirt, ad copy, perhaps an animated mascot, and so on. Eventually you add elements to enable the shop ping process, SKU numbers, prices, sizes, maybe even stock counts. But when you think about it, you realize that more is demanded of this page. The marketing manager must approve a catalog item before it goes live. The licensing college must sign off on any page in which their mascot or logo is displayed. These initials should be right there in the XML, at least in some stage of its life cycle. Model releases, authoring information, expiration schedules, and marketing effectivity data all might be part of this item, depending on who is looking at it and when. You can design an XML object that describes as fully as possible the way this item is seen throughout your client's organization. The happy result of this would be improved work flow and a faster, more responsive organization. This is not to say that all these elements would be present in any XML presentation. No user would want to see all these things. But the data design should be able to encompass any of them.

By the way, it is quixotic to try to anticipate every use to which your XML object might be put. Don't worry. The X in XML stands for eXtensible. New elements can be added later to your design, both globally or as a special extension.

Industry Standards

Before starting the big job of designing a sturdy data object, consider this: Your client is a member of a community. In most communities, you will not be the first to try to apply XML solutions to the common problems. There may be a committee-driven or de facto standard already in place. By building on such a standard, you will put your client in the market, not attempt to build a private market.

Example

You have been hired by a large real estate broker to provide a slick web browser for their real estate database. Assuming that their data is not in XML form, you will need to create an XML vocabulary for their server to communicate with your Flash browser. You must think through the data requirements and carefully develop a data object that will support all the features you want, now and in the future.

Or you can do some research and discover that this job has already been done. RELML, the Real Estate Listing Markup Language, is gathering industry support. By adopting this as your data format, instead of developing your own, you collect all these advantages:

  • You have saved considerable work in designing and defining the data.

  • You may save more work if you license existing RELML middleware.

  • You have rich sets of test data.

  • Other software—browsers or bots—can easily shop at your client's site.

  • Your client can expose listings from other databases.

  • A great deal of your development work can be reused on other real estate projects.


Conclusion

A subtle intellectual breakthrough combined the flexible power of data structures and the benign legibility of text. The result empowered both the world of publishing and the world of data sharing.

The World Wide Web began with that abstraction and for a while managed to balance being pretty with being smart. But it became a mass medium, intoxicated with good looks. HTML became hostage to the demands of graphic designers and lost its sense of abstraction. The most popular presentation technologies, like Flash, ignored data concerns and focused entirely on, well, flash.

But the pendulum is swinging back. The need for XML was felt, and it appeared. XML picked up the serious job of abstraction and freed her tarty sister HTML to become a language about how screens look.

XML now emerges as a publication format for very pure content.


Chapter 5. XML Structure

In this chapter we closely examine the XML specification and learn how to create a well-formed XML document. The relevance of each topic to ActionScript is considered, often in the light of an evolving implementation.

Readers with moderate experience in XML might find this chapter useful. Each time the XML specification is restated in English, a new perspective is formed. XML workers might find a tiny highlight in this formulation that rounds out their mental image, if only by raising objections.

Even busy XML experts should read the sections entitled Flash Context. These sections point out the differences between the XML specification and the ActionScript implementation at the time of writing and as anticipated in the future.


Element

An XML document is simply a tree composed of a hierarchy of elements. In fact the document (exclusive of its optional prologue and extremely rare epilogue) is a single XML element. Since an element includes its child elements, a thousand-element tree, if properly formed, is also a single element—the one at the root.

Flash Context

An element appears in the Flash dataspace as a node. The root element appears as the only child element of the document. Each element has multiple pointers into the hierarchy: parent, siblings, and children. Each carries a list of the names and values of all its attributes. In sub-sequent chapters we become familiar with the precise arrangement of these data, and we become comfortable tracing and manipulating them.

Syntax

An element is bracketed by a pair of tags. The information between these tags is the content of the element. The content can be nothing (it often is) or it can be a hideously complex and bulky data structure with hundreds of other complicated elements (it often is). Attribute information, if present, must be embedded in the start tag.The form of the element ID:

<start tag>optional content</end tag>

In the case of an empty element (which contains no content, though it has a name and may have attributes), the start tag and end tag are immediately consecutive and can be fused into a special symbol:

<empty element tag/>

Rules

Each element must have

  • a start tag, introducing its Name

  • an end tag (in some cases these two tags are fused)

Additionally, every element can contain any, all, or none of the following:

  • More elements

  • Text

  • Attributes

  • Entity references

  • Processing instructions

  • Comments

It may not have

  • XML declaration

  • document declaration

  • unterminated elements, references, comments, processor instructions

  • duplicate assignment of attribute

Examples of Elements

<Address>125 Main St</Address> | Address element with street number as content
<Address>Fourscore... earth.</Address> | Another address; it is up to the XML author
<Address line="city">N.Y.C.</Address> | Element with content and attribute assignment
<Address>New York <b>City</b></Address> | Element whose content includes another element
<Address line="apartment"/> | Empty element with an attribute
<Address/> | Empty element containing only a name token

Bad examples

<Address line="optional" line="zip"/> | Multiple assignments of the same attribute
<Address>New York <br>City</Address> | Incomplete inner element
<Address>New York <b>City</Address></b> | Incomplete element: improper nesting


Name

A name describes an element. It is not unique: lots of elements can have the same name. Therefore, a name cannot be used to identify an individual element. Instead, it tells us the kind of element, similar to declaring the class of an object or the type of a variable. There are no predefined element types and (with a single exception explained later) there are no reserved element names. It is up to the user to create and maintain meaning.

Flash Context

Lexically, XML names are similar to ActionScript names. XML is slightly more permissive. There is a potential for trouble from two characters, the colon (:) and the period (.). These have special meaning in ActionScript but are permitted in XML names. Yet even in XML they imply a sense of namespace hierarchy. The colon is explicitly discouraged (though not forbidden) in the XML protocol. And use of the period seems only to invite disaster. We avoid the use of either one due to their use as namespace delimiters.

Of course there is no requirement that an application map XML names directly to ActionScript names. Most applications that tried to do so would come across an obstacle larger than a dot or two.

ActionScript names are unique within their scope (you cannot have two variables called "foo" on the same level). XML element names are very frequently identical to the names of their siblings. While ActionScript names serve to identify an individual datum, XML element names do not.

Syntax

The restrictions on names are simple. Names can use any letter in the alphabet. The alphabet does not have to be ASCII, Latin-1, or even 8 bit. XML supports Unicode and allows names in the current alphabet. Our examples, however, will all be within the Latin alphabet.

Case matters, but it is your choice. There is no standard practice for use of case in XML. Often element names are all lower case with dashes standing in for spaces:

first-name

Names can use any digits, but they may not start with a digit:

first-name2

XML defines no element names, but it does reserve all names beginning with the three letters "xml" (case insensitive), so xml-test is not a permitted name.

Rules

CHARACTER | INITIAL | INTERIOR | SAMPLE
letters | yes | yes | A B C a b c Å ß ç
digits |  | yes | 0 1 2 3
underscore | yes | yes | _
colon | yes, but[1] | yes, but[1] | :
dot and dash |  | yes | .—
xml prefix |  | yes | XML xml xML

[1] Colons should be avoided due to the namespace connotation.

Examples of Names

ADDRESS | Street, city, etc.
player | Contains personal data, such as an address
team | Probably contains several player elements
_team | Different kind of team element
team.mascot | Just a simple name. The dot has no meaning.
team_mascot | Alternative name
team-mascot | Traditional XML name formation
Formula1 | Digits can be included
test-xml | Noninitial XML triplet is fine

Bad examples

xmlTEST | Cannot begin with xml
9-iron | Cannot begin with digit
team:player | Legal, but very bad form unless team is a namespace


Start Tag

XML data is a serial stream. The start tag informs the receiving software that all the incoming data will belong to this element until the matching end tag is encountered. Very often, more start tags are encountered before the end tag. Each start tag must be matched with its own end tag before the previous start tag can be matched.

Syntax

A start tag is simply an element name framed by angle brackets:

XML
<TAG>

If the element has attributes assigned to it, they belong within the start tag. An element can have multiple attributes, but they must be unique:

XML
<TAG attribute="value">

Space is forbidden between the opening bracket and the name. It is required before each attribute assignment. It is optional before the end bracket.

Start tags should look familiar to anyone who has worked with HTML.

Rules

Each start tag must have

  • the < character

  • a name

  • the > character

Additionally, every start tag may contain:

  • Attribute assignments

  • Whitespace after the tag's name

Examples of Start Tags

<ADDRESS> Street, city, etc.
<player   >
Contains legal but probably useless whitespace
<team   mascot= "wildcat">
Contains valid attribute assignment
<team   mascot="wildcat" color="purple">
Multiple attribute assignment is permitted

Bad Examples

<  player  >
Illegal whitespace before name
<team   color="purple" color="white">
Duplicate attribute assignment is forbidden.


End Tag

The end tag functions exactly like a right parenthesis or a closing quotation mark or a right curly brace. It contains no data of its own; it simply ends the most recent (innermost) tag with the same name.

XML requires a matching end tag for each start tag. Not only must they have matching names, but they must also nest properly. The most recently encountered start tag must be paired with the next end tag. If they do not match, it is a fatal error.

Syntax

The end tag contains exactly the same name as the start tag it closes. Simple punctuation distinguishes it from the start tag:

XML
</TAG>

It cannot include attribute assignments or any other content.

Rules

Each end tag must have

  • the </ character pair

  • the name

  • the > character

Alternatively, in the special case of an empty element, the end tag may be fused with the start tag. This results in an empty tag containing

  • the < character

  • the name

  • optional attribute assignments

  • the /> character pair

Examples of End Tags

<Team> . . . </Team> | Street, city, etc.
<Team> . . . <Player> . . .
    </Player> . . . </Team>-
Proper nesting
<Play> </Play> | Empty Play element
<Play/> | Exactly equivalent to the preceding, using an empty tag
<Play  speed= "normal"/>
Empty Play element with speed attribute
<A><B> . . . <C/> <C/><B> . . .
<C></C></B></B></A>
Complex nesting: A contains only B, which contains data, two empty Cs, and another B. This inner B also has data and a single empty C. All three Cs are equivalent.

Bad Examples

<Team>…</team> | Not a match (case violation)
<Team/>. . . </Team> | End tag paired with self-ending empty start tag
<Team> . . . <Player> . . .
    </Team>  </Player>
Bad nesting


Attributes

Every element can have an unlimited number of attributes. Attributes are a special form of content. They are meant to represent unique properties that are bound very tightly to an individual element. Unlike the element's text content or subelement structure, the attributes tend to be very constrained.

An element can only have a single value for any attribute. A tag that contains two attributes with the same name produces a fatal error.

The XML document definition mechanisms allow us to define attributes unambiguously. We state which attributes each element may or must have. We establish the type of each attribute and we can limit its valid values. Default values can be specified by the data designer.

Validating XML parsers will insure that a document's attributes fit the constraints we've established. No other content can be validated this rigorously, so attributes have an important role in an XML document in transmitting critical data.

Flash Context

Attributes are organized in ActionScript as an associative array, which is a member of the element node object. The name of the attribute becomes the index into the array and the value becomes the string content of the associated array element. Parsing the value into numeric form (for example) is the responsibility of the programmer, although in practice it is often performed automatically by ActionScript's implicit casting operations.

Syntax

Attributes are presented as names and values joined with an equal sign (=).

XML
attrib = "value"

Attribute names must comply with the name constraints we studied earlier. Spaces are permitted but not required. Most important, the value must always be a quoted string.

Rules

Each attribute must have

  • the name

  • an equals sign

  • an open quote

  • a value

  • a close quote

It may include:

  • space.

Attribute values must not make references outside the document they appear in. (We will see later how these references are made in other parts of the XML document.)

Attribute values cannot contain a < character, except as &lt;.

Examples of Attribute

name= "Anna" | Simple value
name= "Anna Leah" | Value can contain whitespace.
name= "&daughter;" | Value can be an internal reference.
url= "http://bigfun.net/index.xlm" | Value can be a complex string.
Speed= "4.5" | Numeric values represented as strings

Bad Examples

url= http://bigfun.net/index.xlm | Forgot the quotes
tag = "<IMG>" | Forbidden character
name = "&//bigfun.net:daughter;" | Illegal external reference
Speed = 4.5 | Forgot the quotes again


Text (Character Data)

Text—or, more properly, character data—is all the document content other than markup. (Markup is the XML codes: tags, comments, declarations, and so on.) The intent of the XML standard is that the significant content of the document is contained in the character data while the structure of the document is encoded in the markup. This intent is generally followed.

All character data is found within an XML element and forms the content of that element:

XML
<ELEMENT>Some text.</ELEMENT>

Elements are often found within text blocks. In this case, the outer element contains both character data and a child element:

XML
<ELEMENT>Some text.<CHILD/>More text</ELEMENT>

The embedded element breaks the character data into two distinct objects that applications can treat separately. When we study the Document Object Model, we will find these two blocks of text in separate nodes. There would then be three children of this element—two text nodes and one child element.

Of course, the child element might also have its own character data, which is not directly part of the outer element:

XML
<ELEMENT>Some text.<CHILD>Child's private text</CHILD>More text
  </ELEMENT>

Flash Context

Within ActionScript, each block of character data appears as an individual node. In the object model, these text nodes are as significant as element nodes. They are differentiated by NodeType and they must always be leaf nodes. Text that is interrupted by an element (as in the previous line of code) appears in memory as two separate text nodes plus the interrupting element (e.g., <CHILD>). This is especially problematic when the content contains even simple HTML markup. For example, it might include the common <u>underline</u> tag.

Syntax

Character data is any sequence of characters in the document's alphabet. The only disallowed characters are the two that introduce XML markup: the left angle bracket (<), which initiates all XML tags, and the ampersand (&), which initiates an entity. These two characters are inserted into character data with escape sequences.

Rules

Character data is composed of:

  • any character (including whitespace) except the < or &.

It may also include

  • escape sequences

  • other entity references

  • CDATA blocks

Examples of Character Data

<Team>Duluth Wildcats</Team> | "Duluth Wildcats" is the content.
<EQ>2*2&lt;5</EQ> | Content is "2*2 <5".
<weather>El Ni&#0241;o</weather> | Content is "El Niño".

Bad Examples

<longdistance>AT&T</longdistance> | Forbidden character
<caption>The "<IMG>" tag</caption> | Forbidden even within quotes


Entity References

It is frequently convenient and often necessary to perform string substitution during the parsing of an XML file. The XML entity concept allows such substitution. We will encounter other entity types later, but the only references found in an XML file proper are general entity references. These are references that replace short symbols with legitimate XML character strings. They serve many purposes.

The simplest (and perhaps most common) case is the need to insert into a text stream characters that are meant literally as character data but that the parser would consider as markup. The two characters susceptible to such misinterpretation are the < character, which introduces a tag, and the & character, which introduces, in fact, an entity reference. These two characters can appear in an XML document only when they are serving their specific functions. XML defines entities that substitute for these two. In addition, it provides for a substitute for the > character to satisfy an obscure SGML requirement and for both single and double quote marks, for reasons of convenience that should be obvious.

Additionally a similar escape sequence can exist for any character. The character code (e.g., ASCII or Unicode value) can be expressed in decimal or hexadecimal form. This lets us embed characters that are difficult to produce on the keyboard or unreliable to display.

If the DTD (a preprocessor file we will encounter later) defines it, we can use our own notation, like &yen; to represent the ¥ symbol more clearly than &#165; does.

Of course we are not limited to single characters. The entity can be invoke a string of arbitrary length. An entity is a useful way to represent frequently used and lengthy text. It is even more valuable for representing volatile data. For example, an entity called &webmaster; might list the name and contact information for the person responsible for supporting an XML document. Data encoders enjoy the ability to represent this lengthy, frequently repeated data with a short clear symbol. And if the webmaster position has high turnover, everyone will appreciate the entity's ability to manage volatility.

Flash Context

Few of the abilities of the entity reference are actually realized in the version of Flash available at the time of this writing. The ActionScript parser is declared to be nonvalidating. A nonvalidating parser does not check the XML file against the declarations in the reference DTD file.

The complex capabilities of entity processing—the surface of which we have merely scratched—depend on use of DTD. So it is not surprising to find that user-defined entities such as &yen; or &webmaster; do not function as of the time of this writing.

The five predefined escape sequence entities work as expected, and so does the decimal encoding of single characters. The hexadecimal equivalent is not functional at the time of writing but might be fixed soon, as it seems to be the product of oversight, not architecture.

Syntax

The entity reference generally presumes a matching entity declaration in a referenced document: the DTD.

Alternatively there are five symbolic predefined entities and a mechanism for specifying them with decimal or hexadecimal numbers. All these invoke only a single character:

XML
&#ddd;        decimal character code ddd
&#xhh;        hex character code hh
&amp;         &
&lt;          <
&gt;          >
&quot;        "
&apo;         '

Rules

Each entity reference must have

  • an opening ampersand (&)

  • a valid entity reference

  • a closing semicolon (;)

It may have (in the entity reference position)

  • the # followed by a decimal number representing a character code

  • the pair #x followed by a hexadecimal character code

  • one of five standard character entities: lt, gt, apos, amp, quot

  • any token defined in the DTD

It may not have

  • any characters or sequence not valid in an XML name.

Examples of General Entity References

if (x&gt;min &amp;&amp; x&lt; <max)
    print(&quot;it&apos;s ok&quot;);
if( x >min && x <max) print("it's ok");
&#xA9;2000 Jacobson &amp; Jacobson ©2000 Jacobson & Jacobson
&copyrt;2000 Jacobson &amp; Jacobson Same as above if DTD defines copyrt as #xA9

Bad Examples

&copyrt;2000 Jacobson &amp; Jacobson | If no definition of copyrt exists, this resolves to &copyrt;2000 Jacobson & Jacobson
<ELEMENT attribute=&quot;value&quote;> | Entity references cannot be terminators.


Comments

A comment is text, inserted into the document tree, that is not part of the XML content itself. Comments are meaningful only to humans (when they are meaningful at all). They are typically used by the publisher of an XML document to help other developers extract the most meaning from the document.

Comments are most valuable during the software development phase. During normal XML data exchange they are disregarded.

While most editors preserve comments, not all software will. Browsers, such as IE5, usually preserve both the comment's text and its relationship to the hierarchy of the XML object, allowing comments to be carefully placed where the context is significant.

Flash Context

The Flash XML parser discards incoming comments. This is perfectly legitimate—comments are meant to be read not by applications but only by humans. They are not available to the ActionScript application except by using heroic measures (i.e., writing our own mini-parser).

In fact, older releases of Flash (5.0) cannot properly parse comments. When Flash reads in XML with comments, the entire object model is corrupted.

You are strongly urged to avoid comments in the XML that will feed into your Flash application until it has been tested thoroughly.

ActionScript cannot generate comments in the XML object it outputs. It would be possible to insert comments only "manually" by serializing the XML (generating an XML string from the XML object) and then using string operations to insert or concatenate the comment. This extreme effort should be warranted by some extreme requirement.

If it were necessary to preserve comments in and out of our ActionScript, we would be better off inventing a <COMMENT> element. This would be a normal XML element containing normal text data. It would pass through parsers and code generators in place and intact. It would be incumbent on us, of course, to code our applications to treat it appropriately, and indeed it could be treated quite intelligently.

Syntax

The comment is introduced by the string "<!--" and terminated by "-->". Between these can go any text except for two consecutive dashes (this restriction was introduced for compatibilty with legacy of SGML):

<!--This is commentary.-->

Rules

Each comment must have

  • the four-character string <!--

  • any text

  • the character triplet -->

It may have

  • the & and < characters, forbidden in character data.

It may not have

  • a pair of consecutive hyphens (--)

Examples of Comments

<!-- this is a message from me.--> | Normal usage
<phone>555-0308 <!--use dash--></phone> | Comments can be inside an element.
<town>Kolumbis<!--sic-->, Ohio</town> | Comments can be inside a text string. (Note: This will cause Flash to incorrectly break the text node into two nodes.)
<!DOCTYPE MyXML [<!--by Jesse-->]> | Comments can even be inside a document declaration.

Bad Examples

<town <!--or city--> >New York</town> | Comments cannot be inside a tag.
<!-- It's a--brief--message from me.--> | Double dashes are disallowed.
<!-- If grade is B+ or A---> | A triple dash terminator is disallowed.


Processor Instructions

XML anticipates the need to embed code within a data document. The PI is a mechanism to contain such code. Each PI node identifies the target, the application that will interpret and execute the code. It follows the target identification with the code itself. The XML standard establishes few protocols for engaging this process. It does, however, require parsers to maintain the PI nodes and make them available to the application.

Flash Context

Flash does not like processor instructions. The PI data is not preserved. Even if it were, it could not contain real ActionScript code and make it executable by the application. That would require the Flash player to include an ActionScript interpreter. For reasons of size, it does not. Anyone who has used the eval() operator in Flash and elsewhere will recognize that the Flash implementation is extremely limited. This limitation is to avoid burdening the player with a full interpreter.

But it is worse. At presstime, a bug in the Flash parser misinterprets the PI and recovers poorly. All subsequent data is incorrectly positioned in the hierarchy. Since this bug corrupts the entire XML object—sometimes trivially, often fatally—it would be unwise to include PI material in any XML meant to integrate with Flash. If it is unavoidable (i.e., it is someone else's data), you should check. The PI bug may be corrected by the time you read this. If not, you might need to use an XLS preprocessor to filter the XML (or use a server-based proxy) or you might sign up for some heavy sessions inside the Flash debugger, determining the specific behavior you need to code around.

Syntax

PI begins with <? and ends with ?>. Between these is any text. The syntax is defined by the foreign processing language, not by XML. The XML parser will not interpret this text, nor will it expand any embedded entity references.

Rules

Each PI must have

  • the opening character pair <?

  • the target application (or device) to which the PI is directed

  • whitespace

  • the actual processing instruction stream

  • the closing character pair ?>

It may have

  • any characters whatsoever up to the closing ?>.

It may not have

  • an embedded ?> pair

  • entity references that are meant to be expanded

Examples of PI

<?php counter++; ?> | Embedded line of PHP script
<?xml version='1.0'?> | Standard XML declaration is a PI to the XML processor.
<?xm-replace_text { Click here} ?> | Proprietary instruction of the XMeTal editor

Bad Examples

<?mathpro 3.1?>x**2=>n! ?> | Forbidden characters in string
<?actionscript this.nextFrame(); ?> | Not yet, not yet

Note

XML FOR THE HTML EXPERT

XML will feel familiar to the HTML coder. It has the same notions of start and end tags enclosing very loosely defined text data. They both interject well-specified attribute data within the body of the opening tag.

In practice, XML is far stricter than HTML. Coders used to the forgiving nature of web browsers and the lax definition of many HTML tags should prepare for a more stringent environment. Respect the following differences:

Case sensitivity. HTML is not sensitive to case; XML is.

<Price> is not the same as <PRICE>.

<name> will not be terminated by </Name>.

Explicit end tags. HTML tolerates unclosed elements. XML does not.

Stand-alone <P> and <br> will not work.

<P></P> and <br></br> will work.

Stand-alone <P/> and <br/> will work.

Value delimiters. HTML permits unquoted attribute values. XML does not.

<OBJECT width=100> does not work.

<OBJECT width="100"> works.

Proper nesting. HTML permits element overlap. XML is strict.

"I just <B><I>know</B></I> it!" will not process.

"I just <B><I>know</I></B> it!" will.

XHTML

Since XML is more demanding than HTML but similar in syntax, it is possible to write proper HTML that is legitimate XML as well. This more formal HTML is called XHTML. It is HTML 4.0 that follows the disciplines required to be well-formed and a few others required to be valid.


Conclusion

The structure of basic XML is relatively simple and not unfamiliar to those exposed to HTML. However, it is stricter than HTML.

Many XML features are not implemented in Flash. The features that are missing would be useful in a very complex architecture, but their omission does not prevent XML from being powerful and perfectly useful within Flash.


Chapter 6. XML Validation: DTD

We have seen that XML can describe nearly any data. It offers tremendous flexibility as nothing is predefined. The element names we use have no intrinsic meaning in the XML standard. We make them up as we go along to describe our data.

Similarly, there are no rules about how the single document element organizes its component elements or how those elements are themselves composed. They too are established to fit the data being modeled.

This practice is fragile. To be useful, the producer and the consumer of an XML document must agree on the structure of the document and each of its elements. In this chapter we will learn to create formal declarations of XML vocabularies.

Several formats have been proposed to formalize these designs. The original (and simplest) is Document Type Definition (DTD). We will learn the syntax of DTD, and we will use it to express a data design and validate compliance.

There are other important uses for DTD, and exposure to them is necessary for a solid understanding of XML. But none of these advantages will be exploited by Flash applications here or elsewhere. The Flash XML parser does not parse DTDs directly. Reliance on these techniques will create problems. So we learn to use the DTD only to create macro shortcuts and conditionally compile.


Purpose of DTD

The software that writes an XML document is often written by the same developer who writes the software which reads it. The structure of the XML elements seems obvious. The need for a formal specification might not be felt at all.

As software development proceeds, the data usually becomes more complex and the project often involves more developers. Client software and server software become mismatched as they reflect different stages in the evolution of the data design.

This problem is common when a project starts without planning the data communications. Thoughtful data design is a good investment. It requires a serious commitment of time, but it pays off in stable XML formats. This stability frees the developers of the client and server application from a hobbling dependence.

An XML server must provide the elements the client requires in the expected format. Otherwise, dysfunction is inevitable: the client fails to get necessary information or the server sends signals that the client ignores.

XML is famously resilient. Many miscommunications will still work—more or less. But this level of function can easily cause bugs. These bugs will be more subtle and more difficult to identify than a frank data load error.

The solution is to describe the data structure independently of the file. We create a link in the XML file to a reference file. The reference file describes the XML structure unambiguously. Programmers experienced in languages like C++ will be very familiar with this concept. In such languages, prototypes of the objects in the applications are placed in a shared header file. This ensures agreement between all the source files on their structure.

By placing these shared definitions in a common file, several wins are achieved. Maintenance is simplified and features are simple to publish. Collisions are averted. Best of all, the compiler—not sloppy humans—automatically enforces compliance with agreed-upon standards.

The solution is very similar in XML. Authors of XML data structures publish their design assumptions in an external file to which compliant data files refer. It is enforced by the XML parser.


Valid XML and Well-Formed XML

XML that names an external model and is compliant with that model is said to be valid. The term is very specific; it describes the highest of three states that an XML file can attain.

  • A valid XML entity is a well-formed XML entity that references a published data descriptor and complies with the rules it expresses.

  • A well-formed XML entity complies with all XML syntax rules.

  • A malformed XML entity does not have proper XML syntax. (It is not really XML.)

There are several competing formats for expressing XML data structure. The official format is the Document Type Definition (DTD). It is officially accepted but not unchallenged. Weaknesses in DTD expressivity have led to the rise of competing technologies. The most viable of these seems to be Microsoft's Schema. Schema is still a work in progress, growing in both technical maturity and political acceptance. But in spite of their strengths, other proprietary formats are not as broadly accepted as DTD, which will be our focus here.

It should be noted that not every parser is a validating parser. The Flash parser, for example, is not. It cannot distinguish well-formed from valid files. It will not open the DTD file and will not use the tools supplied.

We could ask why we need to stuff our aching heads with the DTD syntax if it will be ignored by Flash. The reason is sound. Every file we read had to be written at some point. As it is being written, particularly when it is written by a human, there is the potential for errors. The use of a DTD (or Schema, or other data definition) will test the XML—not for its compliance with the XML standard but for its adherence to the higher standard asserted by the application. A standardized DTD makes an excellent way to publish (to outside content providers, for example) the format in which you will accept data. Content publishers and their customers not only can express these formats but easily test them. Each can get confidence from a validation procedure that is as simple as opening the XML file in Internet Explorer.


DTD Declarations

Oddly, the DTD uses syntax that has a confusing similarity to XML syntax but is completely different. A DTD declaration has the form

Open delimiter (DTD mimics XML by using < to open the declaration.)

Declaration (Unlike XML, DTD has a finite set of predetermined declarations.)

Name (Name of this object in the XML file.)

Rule (Defines constraints on the content of this object.)

Close delimiter (>)

Example: <!ELEMENT Quiz ((Q,A,A+)*)> This is an !ELEMENT declaration that declares a type of XML element called a Quiz whose content is a series of Qs, each followed by two or more As.

<!ELEMENT>

This is the simplest declaration. It simply declares the existence of an element type and specifies the content it may have:

DTD
<!ELEMENT name (contents)>

name is a legitimate XML name (see Chapter 5).

contents is a rule composed of the following set of symbols.

<!ELEMENT> Content Rule Symbols

#ANY tells the validating processor that nothing is known about this element. For describing an element itself, this is a waste of bytes. However it serves a need when this element (about which nothing is yet known) needs to be introduced to the processor, so it can appear as content in the declaration of a larger element.

\#EMPTY defines the element as a leaf element with no content. (Leaf nodes are those without child nodes.) This does not mean that the element has no meaning. Such an element might carry a great informational cargo in its attributes. Even a bare element with neither content nor attributes can present great meaning. Elemental tokens by their presence and by their context can be very meaningful.

#PCDATA is parsed character data. This is text without markup except entity references. Of the following two lines, only the first is PCDATA:

XML
He&apos;s coming!
He <bold>is</bold> coming!

An element containing only PCDATA is a leaf node and is more specifically referred to as a node with character content.

elementname The appearance of a declared element name in the content rule marks this element as a parent element, that is an element with element content.

<!ELEMENT> Content Rule Operators

It is not unusual for an element to contain multiple elements or to contain both element content and character content (mixed content). Any combination can be expressed with a set of operators that express combination and occurrence.

Combination

, | The comma states that the two elements it joins must both appear and in this order.
| | The bar states that one and only one of the two elements it joins must appear.
() | Parentheses group rules into symbols to clarify the associativity of the operators.

(dot,star) is a dot followed by a star.

(dot|star) is either a dot or a star.

(dot|(dot,star)) is either a dot or a dot and a star.

(dot,(dot|star)) is either dot-dot or dot-star.

Occurrence

 | A symbol without an occurrence modifier must appear once in this position.
? | A symbol followed by a question mark must appear here once or not at all.
* | A symbol followed by the star appears any number of times or not at all.
+ | A symbol followed by the plus sign must appear any number of times.

 | May not repeat | May repeat
May not be omitted |  | +
May be omitted | ? | *

(Q,A)+ One or more Q/A pairs

(Q,A*) Q with an unspecified number of As

(Q,A,A+)* Any number (even zero) of Qs, each followed by two or more As

(Q, ((A,A)|(A,A,A,A,A)) ) Q followed by two or five As.

((A | B | C )*) Any series of any number of As, Bs and/or Cs in any order.

<!ATTLIST>

The DTD format is criticized for its inability to type content. It cannot specify whether content is text, numeric data, or any other type. For a technology called a document type definition, very little typing is defined. The major exception occurs in the attribute list.

The type of each attribute must be specified. A parameter in the declaration defines its type. However, if you expect to find familiar types like boolean or float or even number, you are out of luck. Take a deep breath and enter a complicated world.

<!ATTLIST> Types

CDATA This attribute contains string data. This might be a line of text or a URL or even a number (55) or a boolean (true). It is a very general specification that tells us very little.

ID This attribute contains a unique identifier. Although a number often makes an easy and reliable unique identifier, it is not accepted by the XML rules. The identifier must be a proper XML name and follow the rules for names considered earlier. Uniqueness is independent of context. An element called <player> Jocko is not unique from <game> Jocko. The ID must be unique only within this XML entity. An ID must obey the rules for XML names. It cannot be an optional attribute; its default must be #IMPLIED or #REQUIRED.

IDREF This attribute links to another element somewhere in this file. It does so by referring to the unique ID of that element. Although infrequently used, it permits the creation of data structures more complex than are afforded by XML's basic tree hierarchy. Complex directed graphs, circular references, multiply-linked lists, and relational database models can be represented with these internal references.

IDREFS indicates an attribute that is a sequence of IDREF symbols joined by white space. It gives us the power to make and manipulate simple lists.

ENTITY compels this attribute to be set to the name of a previously declared unparsed external entity. As we have seen, entities are references to files that are not necessarily XML files (e.g., a JPEG image or an MP3 sound). Naturally, references to such entities cannot be fully expanded by the XML parser, so they are maintained as references.

ENTITIES is just a list of entities joined by white space. These plural types are necessary because, unlike its child elements, all the attributes of an element must have unique names.

NMTOKEN restricts this attribute to a single well-formed name.

NMTOKENS allows a series of names separated by spaces.

Enumerated lists consist of a series of names separated by the | (or) symbol. This list defines the set of values from which the value of this attribute must be selected.

NOTATION can precede an enumerated list and restrict it to notations. These are typically used to describe the properties (e.g., media types) of the files referred to by external unparsed entities.

<!ATTLIST> Defaults

A very useful feature of DTD attribute typing is the ability to define the default value of an attribute. Even more useful is the syntax that allows you three special options. Typically the default value is a simple literal like "true" or "5." But it can also be one of the following symbols.

#REQUIRED in place of a default value informs the processor that a valid XML file must supply a proper value for this attribute whenever it appears. There is no default.

#IMPLIED in place of a default value informs a processor that the final application is better able to supply a default value than is the XML processor. In essence, the processor is instructed that there is no default, but unlike the #REQUIRED case, an omission of this attribute is not an error.

#FIXED preceding a default value informs the processor that this attribute must always have this value. If omitted from the XML, it will be supplied. If present, it must agree with the default or an error has occurred that invalidates the XML.


Entities

In addition to the preceding syntax, which specifies the fine structure of the XML document, the DTD makes heavy use of the entities feature. We saw entities in our survey of XML proper, since most (though not all) entity declarations are valid within an XML document itself. However, it is in the DTD that entities are most commonly defined and encountered.

An entity is a richly overloaded term that identifies a coherent block of content. The entity has two formal parts, a name and the block of content that the name represents. This can be almost any self-contained block of content anywhere. It can be huge or tiny.

The XML document is an entity. This document entity is often associated with the idea of a file. While an XML file contains exactly one document, many XML documents never exist as files. In fact, such entities are most interesting to us in this book: only two of our projects involve an XML file. The rest deal with dynamic, ephemeral data that is passed back and forth between live processes. Each of these communications is an XML document entity.

At the other end of the spectrum, an entity can be as atomic as a single byte. XML has five predefined entities, each of which represents a single character. These five characters are troublesome when they appear in character data (text), as they imply markup. The entity feature is used as an escape sequence to insert these characters where they would otherwise disturb the parser. For example, the entity &apos; represents the single-quote (apostrophe) character.

With the exception of these predefined entities and the implied document entity, entities are created by declaration. The declaration is a simple syntax, behind which lurks a bewildering tangle of variations.

DTD
<!ENTITY name value>

Entities fall into five classes.

Character entities are the simplest. They each resolve to a single character. They include the five predefined entities and the numeric entity, in which a reference to any character can be made with a numeric escape sequence. The form is &#number;. For example, Zo&#235; resolves to Zoë.

Internal general entities are only slightly more complex. These are basic text substitutions that are explicitly defined and referenced in the basic XML file itself (not in a DTD). For example:

DTD
<!ENTITY cpyrt copyright 2001 Jacobson and Jacobson>

That text would be inserted into the following line:

XML
Chapter 5. &cpyrt; All Rights Reserved

External general entities separate the value of the entity from the XML document. To resolve an external entity, the processor must read in a new document that is linked to this entity in the declaration. This action could be used to perform text substitution of large, volatile, or shared text content, where the files referenced contain blocks of straight character data, like

DTD
<!ENTITY address   SYSTEM "http://lincoln.com/gettysburgaddress.txt">
<!ENTITY headline  SYSTEM "headline.txt">
<!ENTITY motto     SYSTEM "../missionstatement">

In these examples, the keyword SYSTEM informs the processor that the resource identifier will be found in the same document system as the document it is currently processing. This means, almost always, the Internet. Specifically, the processor seeks the referenced file beginning at the same location as the current file. In the motto example, the missionstatement file will be found in the parent directory of the directory in which the file with that reference was found. For example, while processing an XML file, we might call in the DTD it references. The motto entity declaration might be found in that DTD. If so, the file is sought relative to the current DTD file, not the original XML.

In addition to the SYSTEM identifier, a PUBLIC identifier can also be established. These identifiers are meant to be used to invoke common standards. For example, the W3C Internet protocol committee has a document that specifies standards for HTML. Every well-behaved web page, especially an XML-compliant one, should invoke the standard with a line like this:

HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">

Without the PUBLIC identifier, every browser, when it opened any web page, would be obliged to refer to the DTD document stored on the w3.org server. The load would be insufferable. Instead, an abstract symbol identifies the standard. The naming convention has fields separated by double slashes (and peppered with white space). These identify, respectively,

- ISO-registered (+) or not (-) organization. (The W3C is unregistered?)

W3C (OwnerID) organization itself

DTD (Public Text Class) type of document

HTML 4.0 Transitional (Public Text Description) subject, version, and status

EN (Public Text Language) language of the document

Catalog systems (there are multiple standards for these) retain local copies of the meaning of these symbols.

This discussion implies another consideration. The external entity can hold far more than simple text. It is more common that the entity contains markup, in particular data definitions, entity declarations, and other production rules.

Sound familiar? It should. The DTD is itself such a document. It is an entity invoked by the XML file. It often invokes other entities.

The DTD is written not in XML syntax but in a sister syntax that is interdependent. All validating XML parsers are capable of reading the DTD. But other entities can be even more exotic.

Unparsed entities are external documents that the XML processor is not expected to understand. The documents can be text based or, for example, a gif image or an audio file. All unparsed entities must be external entities, of course. The parser must be able to tell which external entities are meant to be parsed. It does this with the help of a markup called notations.

Notations are an XML feature that allows the document to identify the content type of external files. Notations are separately declared. Most formally they attach a name to a file definition using a PUBLIC or SYSTEM identifier. In practice, this resolves to one of two forms. A notation can be a MIME content–type identifier that is familiar throughout the Internet. Examples are text/html or image/gif or application/xml. Alternatively, a notation can refer to the application required to process the data. The MIME identification is more solid, of course, since application names and locations vary greatly from machine to machine.

Unparsed entities are differentiated from general external entities by the presence of notation, introduced by the keyword NDATA:

DTD
<!ENTITY snapshot SYSTEM "../photos/annaleah.jpg" NDATA image/jpeg>

So far we have seen four types of entities. Each of them can be declared in an XML file or preferably in a separate DTD. All four entities can be referenced (invoked) in the XML document—but none of them can be referenced within a DTD.

Parameter entities are a special case of entities that can be referenced in a DTD. They resemble general internal entities except that they use a special character. As the ampersand (&) is the escape character that begins any entity reference, the percent symbol (%) introduces a parameter entity reference. The percent sign is also used in the declaration of a parameter entity, which is otherwise identical to the general entity declaration:

<!ENTITY % ENGLISH INCLUDE>
<!ENTITY % SPANISH  IGNORE>

This example demonstrates the utility of parameter entities in the DTD when used in conjunction with the keywords INCLUDE and IGNORE. The preceding definition would be followed later in the document by

DTD
<![ %ENGLISH;  [ ..stuff.. ]]
<![ %SPANISH;  [ ..stuff.. ]]

Conditional sections like these can be as long as you like. They identify different versions of the DTD contents and can easily be reversed by changing the parameter entity declarations.


Namespace

Clearly, as DTDs and their elements are being created all over the world, all the time, name collisions are bound to occur. Our document might call in two different DTD protocols, perhaps from two different vendors. For example, we might want to handle two different formats for news sources, the RSS/RDF format from Netscape and the CDF from Microsoft.

Both define, for example, an element called <title> and another called <item>, but these elements have different meanings and very different structures. If our XML is to use both of them, it must be able to easily distinguish between them. It does so by putting a prefix in front of each one, separated from the original name by a colon. Thus we might have <microsoft:title> and <netscape:title>. Or <cdf:title> and <rdf:title>.

It remains to link the initials cdf and rdf to globally unique names, not casual acronyms. The mechanism for selecting globally unique names is the URI (e.g., URL) system of domain ownership. The line

DTD
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"

appears to reference a document on the web. However, it is merely using this unique location to create a unique namespace. It links the abbreviation rdf to this unique identifier during the processing of this DTD. It is considered good form (though not a requirement) to have documentation of some sort at the named URI.

With this definition and a similar one for CDF, we can easily refer to each sort of item element as

DTD
<rdf:item/>

and

DTD
<cdf:item/>

In fact, we can even define the namespace of the null prefix. With the line

DTD
xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"

we assign to the null prefix the namespace that used to be assigned to rdf. The result is that unprefixed elements like <item> or <title> will have the meaning previously held by <rdf:item> and <rdf:tile>.


Conclusion

We have rounded out our view of DTD. We have discovered a powerful metaformat with which we can shape XML documents. But what good is it to Flash programmers? After all, the current Flash parser is not a validating parser—it will not read in external files such as DTD.

Clearly, there is no value in the runtime use of DTD. But DTD has a critical role in the production process. DTD informs co-developers in your organization and elsewhere how to make XML files that are compliant with your application. It will allow you to test them efficiently.

It is also invaluable in the process of data publishing to both the publisher and the subscriber. It will allow you to produce data for many clients—not all of them created by you, not all of them using Flash.

Also, it will allow you to create applications that comply with formats that have been developed outside your organization. This brings forward the goal of data sharing between organizations and a web not of graphic pages but of high-order information.

In a later chapter we will write to a popular DTD and peek into a worldwide web of data.


Chapter 7. HTTP Connection

In this chapter we prepare for XML connectivity by developing skills in general Internet connectivity. We become familiar with the fundamentals of HTTP transactions and the ways in which Flash uses it. Of particular interest are the restrictions Flash imposes on its users that make it difficult to cross domain boundaries.

We create a Web-driven high score feature for our quiz game, which is its first live online element. To do so, we learn to establish a data download, although not yet in XML format. We also build a probe to interactively test the limits of Flash domain restrictions.

If you have a deep background in the HTTP interchange and in developing interactive web applications, this chapter will be easy. But pay attention to the unique details of ActionScript syntax and the discussion of domains and permissions.

Others might find this discussion of HTTP too deep or too cursory. If it seems too deep, be patient; you will need it all before this book is complete. If it leaves you wanting more, we invite you to pursue the subject further online or at the bookstore.


Characteristics of HTTP

HTTP is the main protocol that powers the web. It is ideal for live interaction between user and server. It is a relatively simple interchange of text messages that allows one computer to request information from another. Besides asking for the file, it supplies information about itself, such as its capability to accept various file types. The server responds with it own messages. In a simple and successful interchange, the server also downloads the file that was requested.

Stateless

A fundamental characteristic of HTTP interchange is that it is stateless. Each transaction is independent. Each request or response is a brand-new interaction between strangers. There is no recognition or memory of previous interactions a week ago or ten seconds ago. Any history—either a long-term account or the record of a simple session (the interactions of a single visit)— must be maintained by the server application. It requires cooperation from the client that must introduce itself to the server again with every mouse click.

Authentication

The strategies for recognition typically include an ID code generated by the server and embedding into the downloaded page, along with the code that causes the client to include this label with each subsequent request. This code might be a simple session ID or a long-term identity, such as a customer relationship. Cookies help maintain both short-term identity and informal long-term history. Password identification and similar tactics are generally part of long-term authentication.

Authorization

Most servers follow NCSA web standards in limiting access to files. Webmasters can apply protection where they desire it. Only users who can supply a valid name and password are allowed access to protected portions of the file structure. These limits are set on a per-directory basis and apply to all the files in the directory and all its subdirectories.

When a page is requested from a protected directory, the user is presented with a dialogue that requests the user's name and password (Figure 7.1). The name and password are tested against a file (.htaccess) that is maintained by the webmaster in the directory being protected. Authorizations can also be set on a per-method basis and can limit only GET or only POST methods. (GET and POST methods are discussed a bit later in this chapter.)

Figure 7.1. Authorization Request

Flash developers must bear in mind the authorization limits on their web. The requests made by the Flash application are subject to the same limits as manual web browsing. If the server with which you are communicating is protected, your Flash application should be served from the same directory. Both the .swf file and the HTML file in which it is embedded must be stored in this directory. The user is asked for a name and password when the HTML file is first displayed. In Figure 7.2, .htaccess protects proprietary and all her daughter directories. Flash will be able to read prop_catalog.xml only if it has been loaded from the /proprietary/index.html page by a user who supplied a valid password.

Figure 7.2. Controlling Web Authorization

Secure Sockets

Authentication and authorization are often steps in establishing sensitive communications (such as a credit card purchase). They are typically transacted using HTTPS, which is simply normal HTTP transmitted over an encrypted socket. This socket is an isolated layer that supports the HTTP layer but does not interact with it. Thus, to the programmer developing an interactive experience, the addition of SSL (secure sockets layer) is essentially transparent.


Structure of HTTP

The two types of HTTP information are header and content.

Content

Content is the familiar material exposed by our browsers. It might be HTML material or swf, gif or mp3. Invisible HTML instructions (such as the ugly block of object information used to embed Flash) and JavaScript are all part of the content segment.

Header

The content segment is introduced by the header. In the header, the client or the server (their headers are similar but not identical) communicates a lot about what it wants, who it is, and what it is sending. Among its interesting functions are the following:

  • Report the status of this HTTP connection.

HTTP
404: File not found
200: OK

  • Identify its user.

HTTP
From: jesse@bigfun.net
Host: 206.33.101.0:8080

  • Direct the interaction.

HTTP
Cache-control: no-store
Connection: close

  • Identify its own software.

HTTP
Server: NCSA/1.3
User-Agent: Mozilla /1.1 (Macintosh; I 68K)

  • List the content types it likes to see.

HTTP
Accept: image/gif
Accept:  */*

  • Establish or report cookies.

HTTP
Set-Cookie: user=JesseJ
Cookie: user=JesseJ

  • Describe the content that follows.

HTTP
Content-type: text/xml
Content-length: 1024

NOTE

HEADERS WITHIN HEADERS

HTTP is most commonly used to transfer HTML. It is easy to confuse the HTTP header with the HTML header. The latter is, naturally, part of the HTML document, which is the content of the HTTP transfer. HTML headers are HTTP content.

This point can continue recursively. A page represented by HMTL will often have a human-readable header—letterhead, headline, banner. This "header" is part of the HTML body.

XML documents are similarly divided into prologue (header) and body. Both are in the content portion of the HTTP exchange.

Upstream HTTP Transfer

There are two main methods with which an HTTP client can send information upstream to the server. They are GET and POST. (There are other variants, like PUT, a protocol that implements some FTP-like functionality. We exclude these from our discussion because they are not supported by Flash.)

GET

A GET request requires no content body. Information to the host is embedded in the URL. A question mark separates the document identifier portion of the URL from the upstream datagram. Any observant web surfer has seen this format often: It is a popular method of querying a server-based application. It has the advantage that a single URL string not only identifies the web resource but also packages a lot of context and query information. This makes it easy for a simple hyperlink on your web site to contact a web-based map server and retrieve a map to your office.

HTTP
http://mapquest.com/cgi-bin/ia_find?link=btwn%2Ftwn-map_results&event=find_
search&Zip=30096-3038

The disadvantages of GET include the fact that it usually exposes data communications to the user and that it is limited in the length of data string permissible. The former is not much of a problem in Flash, but the latter is a restriction imposed by the server to which Flash must conform.

It is frustrating to report that there is no precise limit on the length of the data string. Different software imposes different restrictions, and the restrictions go beyond buffer size limits. Strings are often concatenated by other processes in ways that are hard to anticipate. Some software breaks lines and breaks URLs (the interaction of e-mail and GET strings is a special nightmare). However, for most straight applications, such as a direct request from Flash to a server, 150 characters are often quoted as a safe rule of thumb.

POST

An alternative form of upstream data communications is POST. In this protocol the HTTP header alerts the server that it is followed by a content packet.

Data is transmitted in a format called application/x-www-form-urlencoded, which has the following features:

  • Each variable is preceded by a question mark (?).

  • Each variable is joined to its value with an equal sign (=).

  • Space is replaced by a plus sign (+).

  • These other characters are replaced by hex codes:

    Nonprintable characters

    < >" %{ } |\ ~[]' (always)

    ;/?:@=& (except when used in URL context)

When using loadVariables, Flash will urlencode the variables automatically. If you need to manually encode a string, use the Flash method escape(); to de-urlencode, use unescape().


Loading Data from a File

Our first experiment with data communication in Flash is quite cautious. We load a file from the server and extract a single datum from it. We continue our pursuit of practical utility by adding a simple data-loading feature to our Quiz game.

Such a useful feature would be to display an up-to-date high score. Let us presume that the highest score achieved so far is known and published on our web site. We would prefer to use XML for this purpose, but at the moment we want the simplest path from server to client, and that path is HTTP GET. It is only as the complexity and size of our data loads increase that GET becomes clearly inferior to XML, and soon utterly inadequate for the task. But to move a single scalar variable, HTTP GET makes a fine baby step.

To get URL-encoded data into Flash, use the loadVariables function. It takes one, two, or three arguments: you supply the URL, the (optional) location, and (optionally) the upload technique.

loadVariables(url, scope,upLoadTechnique);

The URL, as we have seen, is a relative or absolute document identifier. If the data file on the Internet and the .swf file is local, you need to prepend http: to your basic URL. Similarly file: is intended to perform the reverse trick.

The scope indicates the context into which these variables will load. Like all ActionScript variables, they are associated with a MovieClip. This location identifies the MovieClip either by name (target), which can be relative or absolute, or indicated by level, an ungainly and unsafe legacy of ancient Flash versions. If this argument is omitted (or the null string ("") is supplied, the context is assumed to be this.

The upload technique will be useful later. With this argument omitted, the loadVariables function call only captures data from the web; it does not return values to the server.

Notice that loadVariables() does not return a value, not even a success/error code, for two reasons. For one thing, the value being collected is arbitrarily complex. LoadVariables() can pull in many values, each paired with a name that ActionScript might not even anticipate. It is difficult to pass back a single variable whose content is so unknown. In fact, this problem is exactly addressed by XML. An XML object is a single string (or a single in-memory data structure), but it can contain information of enormous complexity and scope.

The more fundamental reason is a bit more subtle. It almost invariably trips up beginners, to whom it seems counterintuitive. With an accurate mental picture of how ActionScript executes, it is simple and obvious. The execution of loadVariables is asynchronous. When ActionScript encounters the command, it starts the operation. It then continues the code on this frame, displays the frame, and continues to the next frame. Were it to wait for a return value, the animation would freeze, which is never good. So Flash immediately executes the next ActionScript instruction while launching loadVariables as a background task. Of course the application logic might require the data before it proceeds.

It is a tenet of good interactive design to display animation of some sort during time-consuming Internet loads. Users need to be constantly assured that their computer has not crashed and that the designer's vision still guides their experience. Consequently, developers create preloaders. Ideally, a preloader is some form of progress bar that displays the status of the load operation. More often it is a simple throbber. A throbber is a bit of animation that exists simply to look alive. It distracts the user during a long load and dispels the impression that the computer has frozen up. Examples are Internet Explorer's e-globe and the Netscape comet. Either design is a trick that masks a load-and-play attitude to software. Both are distinctly inferior to a carefully staged data strategy.

Great programmers aim for a seamless experience and see each display of a preloader as an admission of defeat. Instead, they load data incrementally, hierachically, and preemptively. The application quickly loads a small amount of data that makes its basic operations functional. Meanwhile it anticipates the user's needs and preloads datasets. For example, in a quiz game, the initial download might be only enough to launch a login screen. While the player is typing in his name and perhaps reading a joke, the rest of the game is downloading.


Making a Self-Loading High Score Display

We create a highscore MovieClip that functions independently. It must fulfill these functional requirements.

  1. When it starts, it begins to load the data.

  2. While loading, it displays animation.

  3. It continues to test for a successful load.

  4. It displays the data if successful.

  5. It displays a special frame if the download fails.

We can accomplish all this with four frames. The only important graphic element is a text element in frames 2 and 3 that is linked to a local variable called score. It is backed by a simple colored rectangle and title (Figure 7.3). Frame 1 starts the load with

Figure 7.3. highscore Display

ActionScript
loadVariables ("highscore.txt", this);

Frame 2 is labeled LoopIfNotLoaded. It has no code and serves only for loopback. Frame 4 is labeled LoadingError. It has any appropriate graphic or action. In this case, the highscore is a completely optional part of the game. There's no reason to disturb the game if it cannot be found, so the LoadingError state displays nothing at all.

All the rest of the functionality is in frame 3. If the load is successful, it will create a variable at this scope called highscore. When it discovers this variable, the code copies it to the score variable to be displayed and stops.

ActionScript
if (this.highscore) {
     score=highscore;
     stop();
}
else{
     if( (score=tries++) <90){
            gotoAndPlay ("LoopIfNotLoaded");
     }
     else{
            gotoAndStop ("LoadingError");
     }
}

Until the highscore variable appears, tries is incremented, which counts the number of unsuccessful loops. Simply to keep the screen lively, this count is copied to score for display. This display is chosen because it is easy to create and gives the appearance of progress. Since score uses a unique font, we take care to put only digits into this display. If we use only digits, we can include a much smaller font in the swf file.

The count is tested every frame. If the number of retries is not unreasonable, it loops back for more. Otherwise, the ActionScript gives up and ends gracefully in the LoadingError state.

NOTE

MOVIE EXPLORER

The script in a Flash movie is typically distributed across multiple frames. This allows for beautiful integration of graphics and software. But it is often difficult to see what is going on. We end up clicking from frame to frame, and we open and reopen ActionScript windows.

Flash provides a tool to organize the scramble of code into a fairly linear form. The Movie Explorer is a flexible and powerful viewer that allows you to view the structure of your application at whatever level of detail you wish and filtered usefully.

Our example can be exposed all at once in the Movie Explorer. We can follow the program flow within a frame and from frame to frame.


Object-Oriented Implementation

There are alternatives to the technique we chose in the highscore MovieClip. Our technique was not a sophisticated approach: we call the load action and then run through a loop, testing the data to see if it arrived or not. This solution, known as polling, is not object-oriented. Modern object-oriented coding is associated instead with event-driven methods.

In the polling design, the calling function constantly tests the status—both functions are locked together. In an event-driven method, event handlers must be set up. After that, it is the actual occurrence of the event that calls the function. The rest of the software runs absolutely independently.

There are a variety of events that Flash recognizes and to which we can attach handlers. In this case, we will create a method that is triggered by the data event. But before doing so, the code must be rearranged.

Building the highscore Feature with Objects

We can easily change the highscore clip. It now has three static frames:

  • StartLoad frame

  • LoadedOK frame

  • LoadFailure frame

The last two have no code, although LoadedOK has text that displays the value of the score variable.

The StartLoad frame has this code:

ActionScript
function dataLoaded( ok )
{
   score=highscore;
   gotoAndStop( ok?  "LoadedOK":"LoadingError" );
}
loadVariables ("highscore.txt", this);
stop();

The first frame defines the event handler for the Flash data event. (The data event occurs when the loadVariables function is complete.) The script then calls loadVariables, and stops.

Now we need to assign the event handler to an instance of this object. We go to the Scene (or wherever it has been placed) and click on the highscore object. If the first frame is free of graphics, this object is tough to see. It is represented by a white dot when it is not selected and tiny crosshair when it is.

Clicking on the highscore instance allows us to program Object Actions. We want to attach our event handler:

ActionScript
onClipEvent(data){
    dataLoaded( true  );
}

When we compile it, it works. It is object-oriented and event-driven, and we are committed to those techniques. But is it a better solution? Object orientation is an approach to programming, not a religion. It is usually a cleaner, safer path. But sometimes it is not the best way to solve the problem.

This design has two shortcomings. First, it has no timeout event. This oversight prevents us from gracefully managing a load failure. Such problems are hardly uncommon, and software that cannot detect load failure is going to look stupid. It cannot tell the user that there is a problem, nor can it effectively hide the problem. It is poorly positioned to present a preloader display, since it might never come back to clear the display.

Of course, this problem can be fixed. A loop can be created that will maintain a preloader display and test for success while decrementing a time-out counter. This loop would pretty much recreate the loop code that we built in our polling example, and it would have the extra burden of building, installing, and uninstalling an event handler.

Second, the object-oriented design is more fragile. In our previous version, we made a neat plug-and-play highscore board. Any artist could drag an instance of this MovieClip right out of the library and stick it in any level of his own movie. As long as the server provides a score, this high score board will find and display it. Such a simple encapsulated MovieClip would get a lot of use. The object-oriented version isn't so easily incorporated. The user of this object must know that there is work to do after the instance is dragged out of the library. The user must open the object actions of this instance and write code to hook the data event handler to a function in highscore called dataLoaded(). How is the user supposed to know this and have the skills to do it?

A skilled programmer works around this problem by creating a wrapper for the highscore board. The wrapper is another MovieClip within which the instance of highscore appears and which can hide the required housekeeping. It is this wrapper object that is shared with artists and others.

Unfortunately, by the time we have added these extra components, which mimic the natural features of the polling approach, we have lost the usual advantages of object orientation: simplicity, clarity, and reliability. Instead, it is decidedly more complex than its unsophisticated cousin, more difficult to understand, and easier to break (Figure 7.4).

Figure 7.4. Polling vs. Object-Oriented Approach
    I l@ve RuBoard


Data File

The highscore object we just built requires a file called highscore.txt. We make a file by that name in the same directory as our swf file. In it is a single line:

ActionScript
highscore=122

Loading this file will create the variable highscore at the specified scope and set the value to 122. Flash will treat it as the assignment of a string literal, equivalent to this line of ActionScript code:

ActionScript
var highscore = "122";

If a variable already exists with that name, Flash sets that highscore to "122". If not, it first creates the variable, then sets its value.


Flash Download Security

Macromedia has established a security restriction that limits loading data into Flash. Whether you are using the primitive HTTP data transfer or the sophisticated XML functions, you will not be able to communicate with any web server except the one on which your Flash files are located. More precisely, you can only download from (or upload to) the sub-domain in which your swf file is located.

Similar restrictions apply, for instance, to Java programmers. They are confined to a sandbox, a set of restrictions designed to prevent malicious or careless treatment of the visiting computer. But Java developers can simply request permission from the user to ignore this limitation. This request is in the form of a pop-up window that certifies the identity of the developer and specifies the permissions that are requested. Although disturbing and crude, it is a solution to escaping restrictions. Unfortunately, Flash developers have no similar escape route.

There is an interesting exception to this rule. Every time a Flash file is played on the web, the browser can make direct contact with Macromedia.com. While there is important rationale for this design (updating software, for example), it nonetheless remains a security leak. Cautious developers are disturbed by this mandatory sharing of Internet traffic. They spin scenarios in which, for instance, the logs are analyzed to unearth business relationships that are not public but are revealed by patterns of IP traffic.

These scenarios seem farfetched—but so do the violations that Macromedia is trying to keep us from committing. Interoperability and data sharing are growth directions for the web in general and for Flash in particular. Isn't XML a giant step in this direction? While this restriction impedes, for example, the young developer who might want to make a sexy browser for public XML sports scores, it does little to hamper the pernicious hacker, who can easily dig tunnels beneath Macromedia's barbed-wire fence. The easiest tunnel to dig would be to assemble a simple server application that lives in the accessible domain and processes the Flash requests. Such apps repeat requests and responses, speaking directly to Flash on one end and to the illicit foreign domains on the other. In upcoming chapters, we acquire the skills required and build such an application.

One type of Flash project has no such restriction. A Flash projector is a stand-alone application that combines swf content with a browserless Flash player. Flash projectors typically load from disk or CD-ROM rather than the Internet, so they have no home domain to which they are restricted. For this or some other reason, stand-alone Flash has no restrictions on the domains with which it is willing to communicate.

Similarly, a swf file located on the local file system can access any web site to download data, even though they are running in the browser. Even Flash files that are embedded in (or attached to) e-mail can connect to data on any web site. Unfortunately, they work with only certain HTML-compliant and Flash-enabled e-mail systems, depending on the e-mail client, the operating system, and even the ISP. (Forget AOL users!)

We play within the rules. All our examples comply with the restriction that the swf files and the data files (and middleware applications) reside at the same location. At first we work with simple files. We experiment with Flash and save locally, on our own computer or on a LAN. We load data from the same directory or one that can be accessed relatively easily, since this seems to be the most reliable.

We will soon use middleware as the source of our XML data. Middleware needs to run on a real server. During the experimentation process, we can create local files and access the data servers on the web. This will include URLs serving test data for readers of this book.

To make our Flash public, we will need to publish our swf files to the same domain that is hosting the middleware.

Turning the High Score Display into a Permissions Probe

It is easy to fashion our little HTTP loader into a lightweight tool for probing the domain perimeter. First we modify the initial frame. It needs to be labeled, because we will return here. So we label it Start or something even more clever. Then the URL string, which was hardcoded, has to be made into a variable. Checking the variable tries tells us if this is the first time the code is searching for a file. If so, we give it a name to start with. If not, we just reset the counter:

ActionScript
if(tries <=0 ) {    url="highscore.txt";  }
else          {    tries=0;              }
loadVariables (url, this);

Frame 4 needs a new feature or two (Figure 7.5).

Figure 7.5. Text Entry Field

A simple text field is streched across this frame. It is set to use Input Text, as opposed to Dynamic or Static Text. It is linked to the variable url and uses a common font (Figure 7.6). Finally we include a button (we reuse the AnswerButton from our earlier code, but distort it grossly). The button is scripted very simply

Figure 7.6. Input Text Field

ActionScript
on (release) {
    gotoAndPlay ("Start");
}

Now we have a simple swf file that can be loaded in various settings—local disk, web server, and so on—and we can experiment with the Flash rules of data access permissions and their actual implementation.

To actually test access, we simply type in a URL and press the button. If it fails, the URL is outside the domain perimeter.

Adding the High Score Feature to the Game

The highscore MovieClip is made to be independent. It has no data connection with the rest of the fla file. We can literally just drag it into the game and it's good to go!

Open the highscore file. Make sure its library is open. Open the latest Quiz version. Just drag the highscore feature into the quiz game.

Matching Score Display

To make the game more interesting, let's create a personal score display that matches the global high score display.

Duplicate the highscore symbol in the library. Name the copy myscore and double click to open it in the editor.

Simplify myscore by deleting the code layer and all the frames except one. Finally, change the title text from "WORLDWIDE HIGH SCORE" to "SCORE" or something similar.

Return to the base movie. Make a layer called score. Drag the new myscore symbol from the library onto the stage, thus creating an instance of that MovieClip.

Now align the two score panels in the base movie. Since the first frame of highscore is invisible, it is difficult to lay out, even with the alignment features of Flash.

An easy trick is to carefully place two copies of myscore. When you are done, name each with the Instance panel. Name one myscore and one highscore (Figure 7.7).

Figure 7.7. Manipulating the Instance

For the actual highscore, while the Instance panel is open, click the Swap Symbol and change highscore to be an instance of the highscore symbol, not the myscore symbol (Figure 7.8).

Figure 7.8. Changing an Instance's Symbol

Now update the Round symbol. The score display has been a simple text object in the score display layer of the Round. It displayed the points variable. Now the score is displayed by the myscore MovieClip and recorded in the score variable in the MovieClip. Delete the score display layer altogether and change the score updates in the Win and Lose states from

ActionScript
points=++_parent.points;

and

ActionScript
points=--_parent.points;

to

ActionScript
++_parent.myscore.score;

and

ActionScript
--_parent.myscore.score;

Now our quiz is quite handsome (see Figure 7.9). It displays both the player's local score and the public high score, which it loads from the Internet.

Figure 7.9. The Quiz with High Score and My Score
    I l@ve RuBoard


Conclusion

We have learned the elements of HTTP. The basic format of request and response between client and server is what makes the web work.

We examined in depth the security issues common to the web and specific to Flash. We need to understand these features well to use them and even better if we are to overcome them.

We made our first data request and download from the web. In this case the HTTP was simple and it involved no XML. But we soon will use XML and we will later work with the fine points of HTTP.

Our first data-connected object has some interesting qualities. It is very neat and self- contained, independently managing everything from its initiation and data download to its display.

We learned an alternative, more sophisticated route to the same results and faced our first choice between smartness and simplicity.


Chapter 8. XML Connection

We achieved some success in the previous chapter. We connected to the server and downloaded a data file—a simple text file. In this chapter we upgrade to an XML file transfer.

We study the XML parser. This component of Flash interprets all XML data. It reads in the XML and builds a standardized memory structure called the Document Object Model (DOM). We consider the DOM in detail and learn our way around its features.

In our explorations, we use the Flash debugger to browse memory. Once we learn to operate this simple debugger, we can examine data directly while the Flash program is running.

This is a must-read chapter.


Downloading XML

In the last chapter we built a highscore feature that loaded data from the server. This data was in a format called urlencoding (where variables are sent as name = value).

Now we upgrade this highscore feature to XML. We start with a simple XML formulation. Our data is in a file named highscore.xml and its entire contents is the line

XML
<?xml version="1.0" encoding="UTF-8"?><score>122</score>

or just the business end

XML
<score>122</score>

which is simply an element named score with a content of 122. It is useful to remember that 122 in this context is a string value, not a number. All XML content is text.

Opening the highscore MovieClip, we can update the first frame from

ActionScript
loadVariables ("highscore.txt", this);

to the XML equivalent

ActionScript
highscore=new XML();
highscore.load("highscore.xml");

In the non-XML code, the load target is the MovieClip instance specified in the call (this in our example) . In the new style, the target is an XML object. The MovieClip scope (this) is still significant. The XML object highscore that we just created is, in fact, this.highscore. Instead of creating a bunch of variables in this, we have created a single object, and our load will populate it.

The XML approach is, among other advantages, much safer. Consider the vulnerability when we invite a file to enter our namespace and establish variables there. These variables can inadvertently or maliciously overwrite important data.

The highscore.txt file in the first example might have included the statement

?tries=90

which would have lamed our highscore function. By contrast, nothing in XML coding could create such a name collision, intentionally or otherwise.

The XML.load method is much the same as the loadVariables function with which we are now familiar, except, of course, that its context is carried by the XML object on the left of the period rather than as a second argument to the function.

While loadVariables would load or send all the variables in its namespace, the XML.load method transfers only a single complex object with its own hermetic namespace.

NOTE

It is important to point out a critical but subtle detail. In the previous chapter, we created functions in the highscore MovieClip. The functions used the keyword this to address the variables inside their instance of the highscore object. If there is a function myhighscore.func() and it addresses this.var, it refers to the variable myhighscore.var. By contrast, if there is also an object in the highscore MovieClip called myobj and it has a function that refers to this.var, the variable referred to is myhighscore.myobj.var. This might seem obvious, but it is very easy to overlook.


XML.load

Instead of testing to see whether a variable has magically sprung into existence—

ActionScript
if ( highscore ) {
     score=highscore;
     stop();
     }

—we can now test the XML object we have created:

ActionScript
if ( highscore.loaded ) {

Every XML object has a public boolean property called .loaded. Testing this is prettier and a lot more robust than looking for data to appear. Something other than a successful load could trip the original test by causing highscore to appear on this frame. Conversely (and more commonly) a good load of a bad file would give false negatives. By contrast, the XML conditional directly tests the success of the load operation.

Even this improved test is suboptimal. We are still rolling around in loops polling a flag. Modern programmers would prefer an efficient event-driven design. So shall we, but later. We return to this issue after we make the code do something.

First, we make the code reassure us that it is working by tracing out this message:

ActionScript
if (this.highscore.loaded) {
      trace( highscore.toString());
      score=highscore;
      stop();
      }
Output
<?xml version="1.0" encoding="UTF-8"?><score>122</score>

Reassuringly, this is identical to the XML file. However, it is not the original XML string. It is a fresh XML encoding of the object created by parsing the original string. This round-trip can be demonstrated by adding syntactically insignificant characters to the data file, such as trailing space inside a tag—

XML
<?xml version="1.0" encoding="UTF-8"?><score    >122</score>

—or comments (which Flash legitimately discards):

XML
<?xml version="1.0" encoding="UTF-8"?><!--comment--><score>122</score>

Neither of these changes to the input string alters the output string at all.

NOTE

FONT GOTCHA

Note that we did not remove the line that assigns highscore (the loaded data) to score (the variable linked to the onscreen display):

score=highscore;

When we look at the display, however, we see this:

Why is this?

When this code executes, the variable score is assigned the same string we saw in the trace statement.

<?xml version="1.0" encoding="UTF-8"?><score>122</score>

The Flash interpreter invokes the toString() method when assigning a complex object (like this XML object) to a scalar variable—which it considers a string variable. But why does the display show 108122?

When we set up the text field that displays the score, we used a special font. To minimize the download, we saved only outlines for the digits in the font. This is fine when we are displaying actual scores. Letters and symbols, however, are simply not displayed. It is interesting to note that even the minus sign (–) and period (.)—so useful in displaying numbers and so simple in every font—were not automatically included.


Debugging Flash

The Flash application includes several useful tools to examine and tweak our work. The Movie Explorer, for example, is a good way to get the big picture of the structure of a project. At the other extreme is the debugger, which displays the minute details of our project. It allows us to directly examine the operation of Flash code even as it is running!

Debugger

We use the debugger to pry apart our XML object (Figure 8.1). While all the data is visible, it is buried in a cascade of empty pointers. By collapsing the list, we can look at the root object:

Figure 8.1. Debugger

The root node is an element node (nodeType=1) with an empty attribute array, no parent, no siblings, no name, and no value. It has special properties that other elements do not. It has declarations (xmlDecl and docTypeDecl), and flags (loaded and status).

The childNodes array shows a single undefined node (0)—not very useful. But by opening the lastChild object, we see

This daughter node also is a node of the element type with an empty attribute array, no siblings, and no value. But it does have a name: score.

Take a moment and consider this point: An element node directly contains only the data on the tags themselves, not the data between the tags:

XML
<?xml version="1.0" encoding="UTF-8"?><score>122</score>

Although the childNodes array appears empty, it has a lastChild. In that daughter node we can at last find the high score itself. Open it and see the final dead end in the chain of nodes. It has neither children, siblings, nor attributes. It is a leaf node.

Unlike the other two nodes, this is not an Element node (Type 1) but a Type 3—a text (CDATA) node. It is, at last, content:

<?xml version="1.0" encoding="UTF-8"?><score>122</score>

It is a pleasure to have a debugger in the Flash authoring environment, and it brings a sense of professionalism that is sorely needed. But the truth is, it is not very useful for students of XML.

Trace

It is a brutal struggle to try to follow the structure of XML in the debugger. It is inside out and follows a perverse logic. Instead, we will rely on trace statements. Trace statements are a crude and dependable way to examine memory. If we add these three lines, we get a clear picture.

ActionScript
if (this.highscore.loaded) {
      trace( highscore.toString());
      trace( highscore.firstChild.nodeType);
      trace( highscore.firstChild.nodeName);
      trace( highscore.firstChild.nodeValue);
      score = highscore.;
      stop();
   }

We see that the first node is of Element type and has the name score.

Output
<?xml version="1.0" encoding="UTF-8"?><score>122</score>
score
1
null

We can use trace to check score's daughter node—the granddaughter of the root node highscore.

ActionScript
trace( highscore.firstChild.firstChild.nodeType);
trace( highscore.firstChild.firstChild.nodeName);
trace( highscore.firstChild.firstChild.nodeValue);

As we anticipate, it is a nameless text node, containing the actual score as a string.

Output
3
null
122

New Code Means New Bugs

Now that we can find the data and the variable name with which it is associated, we can recreate the function of loadVariables(). We can have XML automatically create or update any variables it finds, turning

XML
<score>122</score>

into

ActionScript
score=122

with a (long) line of code:

ActionScript
if (this.highscore.loaded) {
      eval( highscore.firstChild.nodeName.toString())
        = highscore.firstChild.firstChild.nodeValue;
      stop();
      }

Try it—it works nicely!

The eval() operator tells ActionScript to treat the string ...nodeName as the name of a variable—to create it if necessary—and to assign to it the value of ...nodeValue. This generically approximates the functionality of loadVariables()—and it perpetuates the dangerous behavior we discussed moments ago. Variables are manipulated and generated within our namespace without our control.

The eval() operator introduces a new instability as well. It is very position-dependent. Unless the first node has the name score and its first node is the value we want, our XML loader will fail. It is all too easy to create an imperfect XML file. It is especially easy to unwittingly create extra text nodes from tiny bits of whitespace. Sometimes whitespace is inserted for legibility—by coders used to the transparency of whitespace in Java, C, or HTML. Sometimes inconspicuous whitespace accidentally creeps in. Sometimes it is an artifact of the editor.

Let's take a different approach, one where we test before loading to be certain we have what we want.

ActionScript
if (this.highscore.loaded) {
  if(  highscore.firstChild.nodeName.toLowerCase() eq "score"
   and highscore.firstChild.firstChild.nodeType==3){
         score= highscore.firstChild.firstChild.nodeValue;
         }
    else{
         score= "0000";   // Successful load of bad file.
     }
   stop();
   }

Debugging with DTD

This approach prevents us from loading any value unless it is tagged as a score. It is still very primitive, especially in terms of error recovery. So first we strengthen the data validation. We are testing to make sure that our XML contains an initial element called score with some content. We can easily build a DTD to validate such XML, which can be a useful tool for the publisher in eliminating bad files from the service.

But a DTD cannot validate the content of this element.

XML
<score>Unavailable</score>

This element is perfectly valid to the DTD and may seem appropriate—even clever—to the human who wants to publish it. But we know it will fail our display code (which can render only numerals), and it may fail future game logic that compares the player's score against the global high score.

This may seem like a lot of concern for obscure special cases in a marginal detail of an imaginary application—and of course it is. Later, this kind of concern when applied to very visible details of important web sites will gain you a reputation for professionalism. Expect evenings filled with enjoyable social life rather than caffeine-powered all-nighters staring at a monitor and battling a horde of unanticipated assaults.

Let the DTD validate that the score is a number. To do so, we can move the data from the element's contents

XML
<score>122</score>

and make it instead an attribute of an empty element:

XML
<highscore score="122"/>

XML Debugger

Let's revisit our XML tracer. Time for an upgrade: We need to make attributes visible. The attributes of each XML node are listed in an associative array. An associative array (also called a hash) can be thought of as a one-dimensional array that is indexed not by numbers but by keys. Keys are simple unique strings. A traditional one-dimensional array is a list of elements that are addressed by their order in the list, creating pairs of elements and indices. (The latter are calculated on the fly.) An associative array pairs keys and values. The order is unimportant, and even if the keys are numeric (a perfectly valid associative array), holes do no harm.

ActionScript
if (xml.loaded) {
  trace( xml.toString());
  l=xml.childNodes.length;
  trace( "----------"+ l +" nodes-------------");
  for( var i=0; i<l; i++){
    t=xml.childNodes[i].nodeType;
    trace( "type      "+(t==1? "Element" :
                         t==3? "Text" :
                               ("unknown: "+Number(t))));
    trace( "name      "+xml.childNodes[i].nodeName);
    trace( "value     "+xml.childNodes[i].nodeValue);
    for (name in xml.childNodes[i].attributes)
      trace( "attribute " + name + ":  "
                        +  xml.childNodes[i].attributes[name]);
      if( ll=xml.childNodes[i].childNodes.length )
        trace( " --------"+ ll +" subnodes-------------");
      for( var ii=0; ii <ll; ii++){
        t=xml.childNodes[i].childNodes[ii].nodeType;
        trace( "  type. . .
      etc..

This listing combines a series of upgrades, mostly to generalize this function and clarify its display. It still needs more—especially when we move on to recursion in the next chapter. We will continue to develop this as a casual tool for poking into Flash's XML DOM. The following changes are included:

  • All graphics and sounds have been stripped out.

  • The name has been changed to xmltrace to prevent confusion.

  • The first three generations are displayed.

  • Node types are displayed better.

  • The child node tree is indented properly.

In addition to these fairly cosmetic changes, we have added code to scan through the attribute array. ActionScript includes a variation of the for statement that scans arrays:

ActionScript
for (var in arr)

returns the next key of associative array arr as a string in the variable var. Our code traces both, with this result:

Output
<?xml version="1.0" encoding="UTF-8"?><highscore score="122" />
----------2 nodes-------------
type      unknown: 0
name
value
  ................................
type      Element
name      highscore
value     null
attribute score:  122
................................

Making Use of the Insight

Now that we can find the attribute, let's put aside this experimental xmltrace code and return to the functional code for our trivia game. We use the attribute score from the element highscore.

ActionScript
if (this.highscore.loaded) {
 if(  highscore.firstChild.nodeName.toLowerCase() eq "highscore" ){
         score =  highscore.firstChild.attribute[ "score" ];
       //score = highscore.firstChild.firstChild.nodeValue;
         }

Notice that the new code is far more lucid than the line it replaces—as well as more stable. But it hurts to throw away code; the temptation to rescue this commented-out line of old ActionScript is immense. We can save it with the time-honored programmer's friend: feature creep. We can show more than simply the score.

ActionScript
if(  highscore.firstChild.nodeName.toLowerCase() eq "highscore" ){
         score =  highscore.firstChild.attribute[ "score" ];
         winner = highscore.firstChild.firstChild.nodeValue;
         }

Now we simply change the static text "worldwide high score" to dynamic text that links to the variable winner (Figure 8.2).

Figure 8.2. Adding a Winner Display

Now if we supply an XML file with the winner's name, it will easily be broadcast to all the players. For very little effort (on the client side anyway) we improve the game value of the highscore feature, mixing bitter jealousy and sweet ambition into a recipe that heretofore included only a bland sense of generalized competition:

XML
<highscore score="122">CHAMPION:  MARY T. O'BRIEN</highscore>

When we read this new file, we see

This is a great feature to put into our client, but it needs one more touch. We want to be backward-compatible. Our client should be able to read XML generated without a winner's name. (It is already forward-compatible—this new XML does not break the earlier client that does not read winner's names.)

ActionScript
if( highscore.firstChild.nodeName.toLowerCase() eq "highscore" ){
    score =  highscore.firstChild.attribute[ "score" ];
    if (  highscore.firstChild.firstChild.nodeValue )
        winner = highscore.firstChild.firstChild.nodeValue;
    }

It will only change the label on the high score box if there is text supplied by the XML.


Event-Driven Functionality

Now that our XML reader works, let's break it. We will fundamentally reengineer the highscore object. Modern programs avoid polling when possible. But our function now is spread over four frames, as it loops around and tests the xml.loaded flag. We can do better.

onLoad Callback Function

Flash encourages us to create our own function and install it as the onLoad method. The code we install there is called (by Flash, not by us) when the whole XML file is loaded or when a load failure is recognized.

Formalism

The syntax is relatively simple: You declare a function and then set your xml.onLoad equal to it:

ActionScript
function gobblexml( loadedOK ){
    if ( loadedOK )
        { do stuff}
}
xmlobject= new XML();
xmlobject.onLoad= gobblexml();

Or you can be more concise by combining the definition of the function with its assignment to onLoad. In that case there is no necessity to give the function a silly name.

ActionScript
xmlobject.onLoad= function ( loadedOK ){
    if ( loadedOK )
        { do stuff}
}

Execution Context

The onLoad handler is bound, clearly, to a very specific XML object. In turn, this object exists within a MovieClip instance, on a particular timeline, with its own variable scope. One would expect that when onLoad executes, it will be able to address as siblings other objects on the same timeline, that it would address the parent clip as _parent, and that it could direct its own timeline execution with play() and stop(). But one would be wrong. One would, in fact, pull out one's hair in big clumps, one would shake one's fist in the air, but one would not be able to address any variable or MovieClip in the application. Eventually one would realize that the context of onLoad's execution seems to be at the root. All relatively addressed objects are reached as if called from the root. One would not be particularly pleased. Addressing objects from the root (essentially using absolute addressing) is a terrible coding practice. It makes for brittle, dangerous code. One wants flexible objects—like this highscore feature that can just be snapped into any game and behave as a little independent XML receiver. It certainly does not know or care where it lives in the object hierarchy.

In fact, onLoad executes within the private context of the XML object to which it is linked. (Though this context seems aligned to the root, even that is not guaranteed.) The object's properties include nothing that indicates its own address space. (By contrast, a MovieClip has _parent and _target.) So how does the onLoad code address the clip it lives in?

The answer is delightfully simple. The programmer can create the missing property. After constructing the XML object and before releasing it into the asynchronous load, the initialization code creates a variable in the XML that points back home:

ActionScript
YourXML.clip= this;

works fine. Inside onLoad, we can address a sibling variable as this.clip.foo.

Upgrading highscore

All the logic for this object is now in frame 3. We can haul it all out and repackage it inside the onLoad function. It cleans up pretty well in the process.

There are getting to be too many things named highscore, so the high score XML object is renamed hsml. The highscore MovieClip is pointed to by hsml.display:

ActionScript
hsml= new XML();
hsml.display= this;
hsml.onLoad= function (success )
{
  if ( !success )
         this.display.winner = "HIGH SCORE data is unavailable";
  else{
     if( this.firstChild.nodeName.toLowerCase() ne "highscore" )
         this.display.winner = "HIGH SCORE data is corrupted";
     else{
         this.display.score =  this.firstChild.attributes[ "score" ];
         if (this.firstChild.firstChild.nodeValue.length)
            this.display.winner = this.firstChild.firstChild.nodeValue;
         }
     }
   this.display.nextFrame();
 }
  hsml.load("highscore_winner.xml");
  stop();

An anonymous function is made the onLoad handler for the hsml object. The load is initiated, and the MovieClip is stopped on this first frame. There are absolutely no graphics. We just wait for onLoad to be called.

When it is called, onLoad is passed a success code. If the code shows failure or the XML isn't recognized, the handler selects the appropriate error message. (We will be using the display line meant for the winner's name.)

If the code passes those two tests, it records the score (using the pointer we made to the MovieClip instance). If there is a winner's name, it records this as well. It then proceeds to the next frame.

The second frame is all graphics and no code (Figure 8.3). The movie just stops there and displays the information it extracted from the XML file. All this proceeds asynchronously; we just set it up and forget it. We just throw this object anywhere and it'll just plain work. In fact, it will work in any game as long as the website that serves the game keeps a highscore_winner.xml file in the same directory.

Figure 8.3. The Second Frame
    I l@ve RuBoard


Conclusion

We now have a very sweet little object. All the code is on a single frame. All the graphics are on the other frame. It is completely independent. It works at any level of the object hierarchy. All the action is driven by events: It is instantiated at some point (in this example it starts when the movie starts, but that is certainly not necessary.) This event causes the XML to be created and the load to begin. Then the load itself causes the object's display of its data or its error messages.

We have done a good job on a trivially simple XML file. Next we will undertake complex XML structures.


Chapter 9. Recursive Approach

This chapter highlights improved programming concepts. It focuses on event-driven code and recursion in the abstract, then as used in ActionScript and as applied to the example code we have been developing. We also try a few approaches to text filtering for whitespace removal, and we face some problems of extracting data from XML.

In doing so we build a graphic browser for XML— one that functions as an interactive probe into the data structure. The browser extends earlier work we did on a text-based XML tracer. We create event-driven callback routines and recursive tree-walkers.


Event-Driven Code

Looking back at our general XML display code, we see that the problems with our current design are glaring. It is procedural in all the worst ways. It executes sequentially rather than on events, and it elaborates itself through three handcoded levels of hierarchy rather than through infinite depths. XML does not follow such a limit. So we go again into the code and rebuild it from the inside out.

Return to our xmltrace code. We first restructure it as event-driven code, paralleling the work we did at the end of the last chapter. All of the xmltrace object is now a single frame (so is the base movie), and the active code is all packaged into the onLoad handler. In part, that function looks like this:

ActionScript
xml.onLoad = function ( ok ) {
if( !ok ) {  trace ("failure"); return;}
trace( this.toString());
l=this.childNodes.length;
trace( "----------"+ l +" nodes-------------");
for( var i=0; i<l; i++){
  t=this.childNodes[i].nodeType;
  trace( "type "+(t==1? "Element" : t==3? "Text":("unknown: "+Number(t))));
  trace( "name  "+this.childNodes[i].nodeName);
  trace( "value "+this.childNodes[i].nodeValue);
  for (name in this.childNodes[i].attributes)
    trace( "attribute " +name+":  "+ this.childNodes[i].attributes[name]);
    if( ll=this.childNodes[i].childNodes.length )
      trace( " --------"+ ll +" subnodes-------------");
    for( var ii=0; ii<ll; ii++){
      t=this.childNodes[i].childNodes[ii].nodeType;
      trace( " type "+(t==1? "Element" : t==3? "Text":("unknown:
        "+Number(t))));
      trace( " name "+this.childNodes[i].childNodes[ii].nodeName);
      trace( " value "+this.childNodes[i].childNodes[ii].nodeValue);
      for (name in this.childNodes[i].childNodes[ii].attributes)
        trace( "attribute" +name+":"+
  this.childNodes[i].childNodes[ii].attributes[name]);
        if(lll=this.childNodes[i].childNodes[ii].childNodes.length)
          trace( "   ------"+ llll +"subsubnodes-------------");
        for( var iii=0; iii<lll; iii++){
          trace( "    name
  "+this.childNodes[i].childNodes[ii].childNodes[iii].nodeName);
          trace( "    type
  "+this.childNodes[i].childNodes[ii].childNodes[iii].nodeType);
        }
     }
    }
  }

This code turns our previous code into an event handler. It rewrites the standard event handler. The first two lines set up the event handler and then make sure that no error exists in the event handler call. The rest of the code has all been seen before, with the exception of this, which now appears in a great many places in the code. xml was changed to this because the code no longer is external to an object but rather is a function of the object. Hence this refers to xml, the object that owns this code.


Recursive Design

The flaw here is obvious. The code works all right, but it is severely limited. It is hideously ugly code—far too redundant, too specific. There is too much code and ironically it does too little: we will only see the generations we have explicitly coded. This version goes down three generations from the root. Some XML will look fine. Some will show only a truncated picture of the data.

We can clean up the code by factoring all the display code into a function call. Note that we need to hook this function to the base XML object. This causes it to address the XML object's namespace. It shares this namespace with the onLoad function that will call it.

ActionScript
xml.shownode = function ( x )
{
  trace(    "type      "+ nodeTypeName[ x.nodeType]);
  trace(    "name      "+ x.nodeName);
  trace(    "value     "+ x.nodeValue);
  for (name in x.attributes)
      trace( "attribute " +name+":  "+ x.attributes[name]);
}

Now we can replace the large, ugly blocks of code with a function that will provide the output we want. We must be careful that the nodeTypeName array, which we refer to in the preceding code, is created in this same namespace as the function.

ActionScript
xml.onLoad = function ( success ) {
 nodeTypeName = new Array(
         "",
         "ELEMENT",
         "ATTRIBUTE",
         "TEXT",
         "SECTION",
         "ENTITY_REFERENCE",
         "ENTITY",
         "PROCESSING_INSTRUCTION",
         "COMMENT",
         "DOCUMENT",
         "DOCUMENT_TYPE",
         "DOCUMENT_FRAGMENT",
         "NOTATION"
           );

This array provides a mapping of the nodeTypes (numbers defined in the XML DOM spec) to human-readable names. But it is a bit quixotic. Despite this heroic list of 12 node types, only two (Element (1) and Text (3) ) are supported as of this writing by Flash. (Text is the same as CDATA, Character Data.)

ActionScript
  trace( this.toString());
  this.shownode( this );
for( var i=0; i< this.childNodes.length; i++){
  this.shownode( this.childNodes[i]);
  for( var ii=0; ii< ll=this.childNodes[i].childNodes.length; ii++){
   this.shownode( this.childNodes[i].childNodes[ii]);
   for( var iii=0; iii<this.childNodes[i].childNodes[ii].childNodes.length;
                                                                    iii++){
      this.shownode(this.childNodes[i].childNodes[ii].childNodes[iii]);
      }
    }
  }
}

This code is a rewritten copy of the previous code in the chapter. Some of the code was not repeated, such as the function declaration. Nevertheless, it is obvious that the code is still horribly ugly. The explicit names of the multigenerational arrays are clumsy intellectually as well as typographically. Though they look neater, they are still arbitrarily limited. We need to step up to real recursion.


Recursion

Recursion is a programming technique where one function calls itself to iteratively process a problem (Figure 9.1).

Figure 9.1. Flowchart of the Recursive Process

RECURSION

If you don't get it, see recursion.

It is important to note that each recursion must depend on the result of a conditional test. If not, we would end up with infinite recursion, a similar problem to an infinite loop.

INFINITE RECURSION

See infinite recursion.

Suppose we create a factorical function. The factorial of a number is the product of every positive nonzero integer less than or equal to itself. So, for instance, 4! (four factorial):

Through a little reworking we see that the general solution is x * (x – 1)! .

The recursive potential should be clear.

ActionScript
factorial (x) {
   return  (x==1)?   1   :    x * factorial (x-1);
}

Recursive functions and loops are very similar. Theoretically, anything that can be written using one can be written using the other. But usually one solution is the natural choice. Churning through a list is easily handled by a for loop. Calculating a factorial is naturally a recursive function.

To get more technical, the major difference in the execution of a loop and a recursive function has to do with the stack. An advanced discussion of the stack is beyond the scope of the book, but some familiarity is required to understand the difference between calls and loops.

The stack stores the current state immediately prior to a function call. Consider these two implications. First, when the recursive function returns, the calling function continues through the codeblock from where it was called with the original values (except what the called function returns). By contrast, variables of loops do not revert to their original values. Second, a stack overflow error can occur. While a loop can go through nigh-infinite cycles fairly easily, every time a recursive function calls itself the stack grows. After the stack gets too large, errors occur and programs crash.

Tree-walking (following all the members of all the generations of a hierarchical structure) is a nasty process using linear looping techniques. Just look at the nasty explicit addressing in our previous code. It is ugly and still performs only a limited job.

Suddenly, in the recursive solution, all is clean.


Recursion in Practice

First we streamline the shownode function. (This function is independent of the recursion but part of tightening up the code.) The function is renamed trace(). The flabby generic node printer is replaced by specialized single-line traces for element, text node, and attribute.

But the real action happens after the node data is printed on the screen. Trace scans through the array of child nodes and installs itself in each as the .trace() method—which it immediately invokes. (Alternatively, we could have added trace() to XML.prototype.)

Of course, this method attaches itself to the next generation and its children and propagates through the entire XML object, displaying itself no matter how broad or how deep the XML object is.

ActionScript
xml.trace = function()
{
  if( this.nodeType==1 )  trace(" [Element]  "+ this.nodeName);
  if( this.nodeType==3 )  trace("      text  "+ this.nodeValue);
  for(name in this.attributes)
                    trace("attr—>"+ name+":  "+this.attributes[name]);
  for( var i=0; i<this.childNodes.length; i++){
    this.childNodes[i].trace    = this.trace;
    this.childNodes[i].trace();
    }
}

And here is something very beautiful. All that odious onLoad code, with its childNodes of childNodes of childNodes, has disappeared. In fact, except for a check of the success flag (demonstrated later), the onLoad functionality has disappeared into the trace function. And this gives us not only a neater (and far more flexible) code base but one that is completely free of the arbitrary limits imposed by the hardcoded method. Note that the trace function, after tracing the appropriate type, value, and attributes, merely traces all the subsequent nodes. These in turn trace the appropriate type, value, and attributes and then trace their children. This recursive approach allows us to move through infinite levels of complexity while keeping the code neat and clean.

ActionScript
xml.onLoad = function ( ok )
{
   if( ok ) this.trace();
   else trace ("load failure");
}

Indentation

It is inevitable (but unnecessary) that we want to pretty up this printout, now that it is so smart. An easy way to do so is to install an indentation string at each node. Each time trace installs itself into a new generation, it concatenates some whitespace onto the current indentation string. (This solution is not the most memory-conserving design.)

ActionScript
xml.trace = function()
{
  if( this.nodeType==1 )       trace( this.indent + " [Element]  " . . .
  if( this.nodeType==3 )       trace( this.indent + " text)   " +  . . .
  for(name in this.attributes) trace( this.indent + "   +-attr-->" . . .
  for( var i=0; i<this.childNodes.length; i++){
    this.childNodes[i].indent     = "   |" + this.indent;
    this.childNodes[i].trace   = this.trace;
    this.childNodes[i].trace();
    }
}  [d]

Filtering Blank Nodes: Three Strategies

As we use this on a number of XML files we notice a problem. Many editors produce files with extraneous whitespace: line breaks, indentation, and visual spacing meant to format the XML for the human eye. This meaningless space seems to be outside tags and is thought to be as invisible to the XML processor as similar whitespace is to the processor of C or JavaScript. Or ActionScript or HTML, for that matter.

In modern ActionScript, this problem can be avoided by setting the XML ignoreWhite property to true. But some of the old ActionScript parsers properly render these as annoying text nodes containing only whitespace. Although this was a technically correct way to handle whitespace in XML, it was useless and annoying. Several ways of filtering out the whitespace nodes were invented. Three are included, as these approaches are valuable instructive tools.

This will be our first step away from just representing the actual received data structure and instead trying to manipulate it.

OUTPUT FILTERS

First we make a boolean function that identifies nodes that we consider blanks. A blank node has no printable characters (ASCII value greater than 0x20(32)).

ActionScript
xml.isBlank= function ()
{
   for( var i=0; i<this.nodeValue.length; i++)
        if( this.nodeValue.charCodeAt(i)>32 ) return false;
   return true;
}

Using this function we do not trace out text nodes that are blank.

ActionScript
xml.trace = function()
{
  if( this.nodeType==1 )                     trace( this. . .
  if( this.nodeType==3 && !this.isBlank())   trace( this. . .
  for(name in this.attributes)               trace( this. . .
  for( var i=0; i<this.childNodes.length; i++){
    this.childNodes[i].isBlank = this.isBlank;
    this.childNodes[i].ind     = "   |" + this.ind;
    this.childNodes[i].trace   = this.trace;
    this.childNodes[i].trace();
    }
}

This makes for a lovely output where structure is indicated quite clearly.

Output
[Element]  null
   | [Element]  Quiz
   |   | [Element]  Name
   |   |   | text)   Dov's quiz
   |   | [Element]  Question
   |   |   | [Element]  Q
   |   |   |   | text)   The Prime Meridian intersects which of these?
   |   |   | [Element]  A
   |   |   |   | text)   Greenwich,  Connecticut

However, it is disquieting. The problem of meaningless whitespace nodes is real. But this solution violates our principal of honestly visualizing the Flash data structure of our XML. In fact, what we are doing here is arguably the opposite. We are diagramming what we wish the data structure were, rather than what it really is. It is inevitable that this ostrich reflex will bring us sooner or later into a painful blindside collision with reality.

It is more honest to show exactly the in-memory image and to fix what is in memory. By far the optimum solution is to use clean files. The problem goes away if we are in a position to insure that our files will not have been formatted for human eyes with whitespace. But that is not possible in many environments. We may not own the data: it might be legacy or shared data or data being actively published by a third party. Or the tools used to generate and maintain the XML might not allow us to create pure files.

CHARACTER FILTERS

Early encounters with the clean file issue led to clever solutions that cleaned the code. A newsgroup posting signed only G.W., for instance, used xml.toString() to generate a new document in memory. Using (actually abusing) String.split(), he filtered out linefeeds, returns, and tabs, then created new XML structure with XML.parse(). This method solved the problem, but it is tediously slow and dangerously inacccurate. It has failures of both commission and omission. As a simple filter for three characters, it commits the error of removing the characters when they appear (with perfect legitmacy) inside text blocks:

XML
<?xml version="1.0"?><haiku ><content>Caprices of spring--
    sunglasses and umbrella
    both in my suitcase. </content >
<author>Walter Vereertbrugghen</author></haiku>

Additionally, it omits a solution for the problematic space character essential to almost all text but also frequently used for indentation.

NODE FILTERS

The problem is not bad characters, but bad nodes. Our solution is to scan through the XML in memory and delete each artifact node. Our definition of an artifact node is a node that is a leaf node of text type that contains no printable characters.

This approach is still imperfect— it assumes an ASCII-like alphabet and it ignores the possibility of intended empty nodes. But it is fast, neat, and honest.

ActionScript
xml.cleanse= function ()
{
    if( this.nodeType == 3){
      for( var i=0; i<this.nodeValue.length; i++)
        if( this.nodeValue.charCodeAt(i)>32 ) return;
        this.removeNode();
       }
    else {
      for( var i=this.childNodes.length; i--; ){
         this.childNodes[i].cleanse = this.cleanse;
         this.childNodes[i].cleanse();
         }
       }
}

If it is a text node, it scans the text, quitting as soon as it encounters a printable character (anything higher than 32, the column of the ASCII table that divides the control characters from the printables). If it is not text, we scan backward through the array of children. The backward scan is essential, since this is a destructive operation. It is safe to chop away at the tail if we no longer need that index. We are in the position of a tree surgeon who is sitting on the same limb he is cutting. It is safe enough as long as we stay on the right side of the cut.

Note the postdecrement test (i-), which insures that we do not miss the zeroth member of the array.

CONCLUSION

We use the node filter throughout this book. Although it would be easy to merely set the ignoreWhite property to true, doing so would cause problems if any of our viewers were using a version of the FlashPlayer earlier than version 5, build 4.1.


Graphic XML Browser

Our software can read an XML file, clean it up, and examine it in terrific detail. But Flash was not created to print messages to the console. It was created to make graphic, interactive, tactile systems. So let's do so.

This exercise goes far beyond what's needed for our overall goal of building a Flash trivia game. So as a small, informative side project we will build a simple browser.

We port our code from a system of informative trace statements that avoid the difficulties of Flash to one that confronts the difficulties and is rewarded by the strengths. With graphic artistry, we make the XML structure clear to see, and with the interactive arts, we can make it pleasant to touch.

The first step is to decide what model of XML we want to represent. The model of an XML document is different than the same object in memory. In a file or streaming document, the atomic unit is the element. By contrast, the atomic unit of the in-memory DOM (Document Object Model) is the node.

We will ultimately show both representations. We begin by creating a graphic display that displays as honestly as possible the DOM memory image. More precisely, we reflect the DOM as supported by Flash. (The standard permits much richer DOMs or somewhat simpler ones. But our goal is to gain familiarity with the Flash model.) We start with a view oriented to nodes.

The first thing we need is a node viewer, a graphic element that can display any node and show all its characteristics:

  • Type. In Flash, this is either element or text.

  • Name. Only element nodes have names (typically short).

  • Value. Conversely, only text nodes have value (i.e., content).

  • Attributes. Only element nodes have attribute arrays.

  • Relationships. All nodes have a parent node. Many have siblings. Only element nodes have children.

We create a simple node viewer (Figure 9.2). It has a text field for each variable. To keep it compact, the center of the panel is overloaded: Both nodeName and nodeValue are in the same space (the former aligned left, the latter aligned right). This won't be a problem since text nodes use nodeValue not nodeName—and element nodes do just the opposite.

Figure 9.2. Node Viewer

The attribute list is not displayed in full. Attributes are generally infrequent (although that is certainly a style question), but when they appear, they can need a big display. Our solution is to display only a simple number on the panel, the attribute count.

nodeName, nodeValue, nodeType are all dynamic text strings displaying local variables of the same names. The n string is linked to nAttributes (the length of the attribute array). Now we retrofit the trace code.

ActionScript
xml.trace = function()
{
  if( this.nodeType==1 ){
           trace( this.ind+" [Element]  "+ this.nodeName);
           this.clip.nodeType   = "—ELEMENT—";
           this.clip.nodeValue  = "";
           this.clip.nodeName   = this.nodeName;
           }
  if( this.nodeType==3 ){
           trace( this.ind+" text)   "+ this.nodeValue);
           this.clip.nodeType   = "TEXT (cdata)";
           this.clip.nodeValue  = this.nodeValue;
           this.clip.nodeName   = "";
           }
  this.clip.nAttributes  = "";
  for(name in this.attributes) {
           trace( this.ind+" +-attr—>"+ name+": . . .
           this.clip.nAttributes++;
           }
  for( var i= y=0; i<this.childNodes.length; i++){
    this.childNodes[i].isBlank = this.isBlank;
    this.childNodes[i].trace   = this.trace;
    this.childNodes[i].ind     = "   |"+this.ind;
    this.clip.attachMovie("nodeView", "clip"+i, ++_root.maxdepth+5555);
    this.childNodes[i].clip=eval( this.clip+".clip"+i);
    this.childNodes[i].clip._x=85;
    this.childNodes[i].clip._y=i*42;
    this.childNodes[i].trace();
    }
}

The first couple of chunks of new code fill in the data fields that the nodeView wants to display. The last block of code gives each childNode its own nodeView instance. Each new MovieClip attaches to the parental clip (the one belonging to this). Then this embeds a pointer in the child to her new node viewer.

The relationship between the nodes is indicated by relative screen positions. A node's mother is to her left, her sisters are above and below her, and her daughters are on her right (Figure 9.3). Keep in mind that the coordinate system of every MovieClip is relative to its parent. So setting _x to a scalar number (250, say) sets it beside its parents. Making _y a function of birth order lines up the siblings.

Figure 9.3. Simple Quiz in the Node Viewer

Our goal is to make a clear picture of the XML object by building a parallel structure in ActionScript. It is parallel, not identical. The only the linkages between the two are those we carefully maintain. They are not symmetrical, either—in the current code, each XML node can address its corresponding node in the display list, but there is no link pointing back. We will confront this issue in later chapters; it is not important yet.


Interactive Node Viewer

We can quickly add interactivity to this picture by putting a button on the attribute counter (Figure 9.4). By pressing this button, the user moves to the second frame of the display which shows the array of attribute assignments. Releasing the button returns to the original display.

Figure 9.4. NodeView's Layers

This code is attached as the object action:

ActionScript
on (press) {  gotoAndStop (2);}
on (release, releaseOutside) { gotoAndStop (1);}

The attribute list might contain multiple assignments, but we have created space for only two lines (Figure 9.5). The space could be made larger—or better, dynamically sized—in a more robust application.

Figure 9.5. The Alternate State of Her Node Viewer

Draggable Node Hierarchies

We add another button beneath the attribute button but the full size of the panel (Figure 9.6). Pressing this button starts a drag operation until the release event.

Figure 9.6. Drag-Sensitive Area on the Node Viewer

ActionScript
on( press )   {   this.startDrag(); }
on( release ) {   this.stopDrag();  }

With this button, the nesting of nodes in the XML object becomes quite clear (Figure 9.7). As we drag any node, all of its descendants move with it.

Figure 9.7. Teasing Apart an XML Document

Hierarchy Open and Close

Despite the fun of dragging the trees of nodes around the screen, it is still fairly cluttered. It would be nice to be able to open and close branches of the tree the same way the Mac OS and Windows Explorer function.

This is not so hard. By adding a two-position switch to the node viewer, we can jump again between two frames, one representing a closed hierarchy and the other an open one (Figure 9.8). The frame labeled Open shows the twisty in the open position and has this code:

Figure 9.8. Open and Close Twisty of the Node Viewer

ActionScript
for(  i in children ) children[i]._visible=true;
stop();

The Close frame has, of course, the opposite, both graphics and code.


Conclusion

At this point we have a very generic XML node viewer. It is useful for browsing XML in a Flash environment. Furthermore, it was useful as a culminating experience for learning about recursion and tree-walking. The node viewer could be used to help verify XML output by our code.

In its simplicity and versatility, the node viewer is also quite useful as a framework for many XML-based applications. It does the basic work of taking a general XML file and embodying it as functional Flash objects. It just remains for the application to design specialized MovieClips to match the expected XML objects.

The node viewer demonstrates an interesting principle. The in-memory XML, as we will see, is not easy to work with. Without pointers, ActionScript is fairly clumsy when it tries to manipulate data in structures like the XML DOM.

Alert readers will notice that we have transferred all the data that was delivered by the XML document into a MovieClip structure. These are relatively easy to manipulate. We will often duplicate the incoming XML with a native (MovieClip) hierarchy. It models our data accurately, displays it clearly, and allows us to handle it directly.

Soon we will think of the MovieClip structure not as a duplicate of our XML data but rather as the data itself transformed into Flash. The clumsy, invisible XML DOM object can be discarded, and we can have data that is colorful, tangible, and even makes noise!

And when we need it to behave as XML and serialize itself into an XML string, it can do that, too.


Chapter 10. XML Server

We have built Flash software that accesses a server, downloads XML, and displays the XML data. On the server, this data exists physically as a file.

In this chapter we address the server and how it delivers XML. The chapter primarily serves as an introduction to the concept of middleware and server-side scripting. We briefly survey the technology and focus on our choice of PHP. Then we ramp up to speed on PHP and replace our flat files with a transitional model that features two tiers of software.


XML Online

In previous chapters we experimented with a quiz game. Its database is expressed in XML, and we have learned a variety of ways to dissect the data and display it to the user. The actual data is stored on the server as a simple XML file, which is accessed as a whole and selected from by the Flash application.

Data storage in XML files is far more sophisticated and robust than storage in simple text files. It is even, in some real senses, an improvement over sophisticated solutions like Generator and Cold Fusion. Unlike those proprietary, closed technologies, an XML service is simple, public, and open. A wide variety of tools can operate on the data from many different perspectives.

However, a file is just a file. Unless the file is actively altered, the data does not change. So it has the problem of being static.

Additionally, the file must be loaded in its entirety. Any process of selection must be done on the client side (in the Flash program) after the entire file is downloaded. This would be a grave inefficiency if, for example, we have software that presents a random "tip of the day" from a heap of 300 tips. It would be necessary to tax the server, the client, and the user's patience by downloading all 300 messages so that a single one could be displayed.

A third problem is security. The user might be permitted to see just a part of a database for commercial or strategic reasons. It is a compromise of security to send all the data to the user, even if it is supposed to go to a client that makes only certain portions visible. If the file is sent, there are many ways to capture all the data, from simply accessing the XML file directly with a modern browser to monitoring the IP packet traffic.


Dedicated XML Server

The solution to these three problems (static data, bandwidth inefficiency, and security) is to generate the XML from an active process on the server rather than a file. One method is an XML server. XML servers are database management systems that function entirely in XML. Choosing a single record from a bunch of records stored on an XML server is natural and simple. And even though XML servers have great promise, they are just beginning to develop presence. Enthusiasts and early adopters are testing the merits of the concept, and there are many competing systems. Many companies hope to be the Oracle of the next wave, and there are open-source projects as well, such as the 4Thought suite and Apache's Cocoon project.

Experts who know the commercial systems favor Ixiasoft software and especially Resin from Caucho.com. While these tools are sturdy, they do involve risk. This is a rapidly shifting market. Until dedicated XML servers like these are more commonly deployed on the Internet landscape, we will work with more traditional tools.

If the XML server is to reign in the kingdom of databases, it will need to displace SQL, the king of databases on the web and everywhere else. SQL's success is not in data communication—it is clumsy and stiff compared to XML—but in its inherent data-modeling power. Relational databases like SQL use extremely simple mechanisms to express quite intricate structures of data. It is far better equipped than XML to express data that is woven together with multiple relationships. XML is adept at expressing only the single inheritance of tree structures and has few tools for applying constraints on the data. SQL has many tools for designing a database and for efficient communication with it. Although an XML server is fine for simple tasks, like pulling elements out of a list, and excels at managing large repositories of linear content, it is probably a poor choice right now for the tough job of modeling the complexity of real data.

The ideal solution might be a server that accepts and serves XML messages but whose inner engine is a relational database like SQL. Some commercial products are built on that design. But most often, such a system is assembled by hand. That is exactly what we will do next.


Middle-Tier Solutions

Most commonly, building a server that combines the advantages of SQL and XML involves a three-tier architecture. Such a strategy separates the process of delivering information into three different layers. Each is a distinct process. Each is created in different software—and each might be running on a different machine.

The client tier is the software in direct contact with the human being. It is software dedicated to presentation of information and creation of an interactive experience. In our case, this tier typically consists of the browser, with its Flash player plugin and the ActionScript file we have written. This tier communicates with the server using XML as a data language.

The back-end tier incorporates the fundamental activity of the organization publishing the site. It could be almost any process; for example, at http://www.time.gov the back end is the Atomic Clock. But most sites are less exotic, and almost universally their back-end tier is a database management system that maintains the data that drives the site's activity, for example, the inventory of a bookstore or the schedules of an airline or the messages on a bulletin board. The DBMS (Database Management System) is specialized software that allows sites to manipulate enormous bodies of data with tremendous efficiency and reliability. The ability of many users to simultaneously interact with the data is an important standard feature.

The middle tier connects the other two. It is often called the application level. In theory, this level can be omitted. In fact two-tier (client/server) architecture was popular until recently, and even one-tier systems used to be common. (Our local library's card catalog is still available as a one-tier application). But placing an application level between the client and the database has several advantages. The most important is to focus the DBMS strictly on database activity, maximizing the efficiency of the most critical and expensive tier. Also important is the isolation of application code into a layer that understands the business logic of the site. It is far easier to tune the application code in such an environment, and the final system is much more responsive when it is shared among three specialized servers. In our case, the middle tier communicates with the DBMS using the SQL language and to the client using XML. So one of its most important functions is translating between XML and SQL, as well as translating between user interactions and database interactions.

First Steps

In this book we construct three-tier architectures—but not all at once. Three-tier architecture involves creating six software interfaces: Flash must read and write XML; the middle tier must read and write XML to communicate with Flash and read and write SQL to communicate with the DBMS.

So far we have a Flash application that reads XML. In this chapter we write our first server code. All the application will do is create XML and pass it on to the Flash tier.

Application-Level Options

There is a wide variety of technologies to choose from when building a server-based application. The rapid growth of the Internet has forced new technologies forward but it was enabled by earlier generations of development. Older IT problems involved managing enormous databases and doing so with great speed. The web was born in a world where excellent solutions like Oracle or SQLServer were in place to manage data. The problems faced by early web site developers were not so much how to manage databases but how to expose existing databases to an enormous new audience—and how to do so while competing with peers for the attention of an impatient public.

This problem is not computationally intensive. It is time-to-market intensive. Old technologies based on a lumbering cycle of code/compile/install/test are not well suited for this application level. To match the web's high demand for speed of development and modest demand for speed of execution, scripting languages have emerged. These scripting languages are forgiving coding environments with low barriers to entry. They favor easy access to useful functions over adherence to academic design.

Active Server Pages

A server-side scripter has many choices today. One of the most popular choices is ASP. ASP is pure Microsoft—it is built on a Visual Basic sensibility. And it tends to run well only on a Microsoft server, like IIS. It is especially compatible with Microsoft's FrontPage web page builder, with InterDev, its development environment, and with Internet Explorer, the Microsoft browser. This tremendous integration into a proprietary world is the strength and the weakness of ASP. Although many organizations find comfort in being surrounded by wall-to-wall Microsoft certified developers, others feel anxiety when snared by the hegemony of Gates. Many programmers who are used to the conventions of C and its descendants never feel comfortable with the irregular syntax of Visual Basic.

Perl

If syntax is an issue, Perl also is problematic. Perl is the most venerable of server solutions. It has been around since the beginning of the Internet and must be credited greatly for the medium's early stunning growth. While the proliferation of content and simple web pages is due mostly to the flexible functionality of HTML, Perl enabled rapid advances in functionality and in the realization of bold ideas.

But in this era of convergent languages, Perl is an obscure and superannuated dialect. It has a tough learning curve, and because many people handle it clumsily, it is not usually a sound basis for collaboration. Further, each access often requires an individual execution of the Perl interpreter by the CGI mechanism, and it often lacks persistence between pages.

Java Server Pages

JSP is an attractive alternative. The Java scripting environment is fairly open and regular. Despite its association with Sun Microsystems, JSP is far less proprietary than ASP.

JSP is much more than a simple scripting language. It allows authors to write interpreted script command, but its real power is in linking pages with Java servlets. Servlets are application code written in Java and embedded into a page. They allow great power, and the JSP syntax is extremely well integrated with XML. Functionality is packaged well in this system; it is excellent for large development projects involving many developers and many organizations.

But for small teams engaged in rapid development, the ramp up to JSP constitutes a large overhead. As a platform for the examples in this book, it has the same costs.

PHP

Compared to corporate showdogs like ASP and JSP, it is easy to miss the charm of the mongrel language PHP. Next to the purebreds, it is homely. It lacks the formal discipline and the design integrity of its competitors. Their feature sets result from long committee meetings of programmers, architects, and marketers, all as picky as pedigreed breeders. By contrast, PHP has been bred by Darwinian forces. As a programmer feels a need, he creates a new feature. If the feature works, it is often contributed back to PHP's bubbling gene pool. The best features become part of the PHP standard.

PHP has grown like a weed, quickly and opportunistically. Its syntax is a "best-of" mixture, combining a general Java-like (C plus objects minus pointers) syntax with occasional lapses into Perl, like the arcane dot-operator.

PHP is a child of the open-source community. Its native land is Unix. With effort and luck it can operate on an NT server. On NT, it exists as a script interpreter, invoked by the server every time a PHP script (.php) script is served. On a Unix installation running the ubiquitous Apache server, the PHP engine is a part of the server itself. It is a module of Apache and it does not have to start up from scratch every time a script is encountered the way a stand-alone script interpreter or CGI program would.

PHP is easy to use, stable, free, and extremely capable. And it is very well documented. The formal documentation is published in 11 languages and distributed in 10 formats, and each format includes not only the official documentation but the comments (elaborating or contradicting) of users—including you, perhaps. In addition are dozens of tutorials, discussion groups, and code repositories all over the web.

That such resources can develop around a product for which no money changes hands is remarkable. PHP began as the Personal Home Page project of Rasmus Lerdorf. He built it as a tool to create an interactive résumé. He shared this tool with a few people who expressed interest. Two students at Technion Unversity (in Israel) formalized it into version 2. And it has grown since, with contributions from all over the planet. Those two students, Zeev Suraski and Andi Gutmans, are now devoted full time to PHP, along with a modest staff.

In particular, the serious ActionScript community has generally accepted PHP as its preferred server language. (Maybe this is true only of ActionScripters who share their code and knowledge—but those who do not are hardly serious members of a community.)

So we begin to create server applications by addressing PHP.


PHP Basics

In the core alone of PHP are dozens and dozens of constructs, keywords, and functions. There are many hundreds of fascinating functions in the many dozen special-purpose modules that have been contributed and become accepted as standard extensions of the language. We cannot study these in depth here, but we will learn about some of them as they are encountered.

Output

A PHP file is an HTML file. Look at our first exercise in PHP:

PHP/HTML
<html>
<head>  <title>First PHP Page</title></head>
<body>If this seems like plain old HTML to you...<br/>
you are right.</body>
</html>

Its unexciting output:

PHP becomes more than HTML when we actually introduce PHP script into the file. PHP script can be embedded anywhere in the HTML (and in fact can replace all the HTML). It is delimited by any of these equivalent options:

PHP
<script language="PHP"> code   </script>
<?php                   code         ?>
<?                      code         ?>

PHP is designed to make it easy to respond to a browser. The commands print() and echo both send text back to the browser. The differences? Print is a function and follows the usual pattern of enclosing its argument in parentheses. Echo is a keyword in the PHP language (like return) and it does not require parentheses. (Many people are more comfortable using the parentheses anyway, and some of our examples use them. But not this example.) Also, print() returns a boolean success value (true is good), and print is buffered output while echo is not.

PHP
<html>
<head>  <title>Second PHP Page</title></head>
<body>If this seems like plain old HTML,<br/> you are
<?
echo "wrong."
?>
</body></html>

Variables

PHP can do a lot more than echo string literals back to the browser. It's core language is rich and full. It is not unlike ActionScript—with a few more constructs, a bit more depth, and a little less discipline.

Like most languages since von Neumann, PHP has words to describe data and to describe the things you want to do to it. Data is simple. Variables are recognized by the first character in their name, which is always $. They do not have to be declared and they cannot be typed (except in the case of compound types like arrays and objects).

PHP
<html>
<head>  <title>Third PHP Page</title></head>
<body>
<?
$right = "wrong";
echo "If this seems like plain old HTML,<br/> you are $right"
?>
</body></html>

The output of this is

Output
If this seems like plain old HTML, you are wrong

PHP's minimalist variable types, string, int, float, and boolean, are distinguished casually by context when they are distinguished at all. The compound variable types, array and object, are explicitly declared. Arrays are extremely capable data types with a rich set of methods. Objects are less mature and (as in ActionScript) fall far short of the level of object-oriented implementation seen in Java or C++.

String Operators

The concatenation operator is the dot: "bath"."tub" resolves to "bathtub"; .= is a valid construct.

Strings within double quotes are evaluated. (Although the depth of evaluation is not specified, our experience leads us to trust PHP to resolve first-order names in this context but little else.)

PHP
$suffix = "room";
echo "bath".$suffix;
echo "bath$suffix";

returns this to the browser:

Output
bathroom
bathroom

Single quotes create string literals with precisely the same contents.

PHP
$suffix = "room";
echo 'Try "bath$suffix".';

produces this output:

Output
Try "bath$suffix".

This is sometimes useful in composing strings for HTML or for SQL where embedded quoted strings need to be literally preserved. However, we often find that this a clumsy approach when variables need to be resolved at the same time. When we compose SQL queries, we use escape sequences instead.


PHP and XML

We can make a PHP file that serves up straight XML; in fact, it is little more than an XML file itself.

PHP
<?php
header( "Content-Type: text/xml");
echo  '<?xml version="1.0" encoding="UTF-8"?> ';
echo  '<Quiz>';
echo  '<Question>';
echo  'Einstein or Bohr:  Who got the Nobel Prize for Physics?';
echo  '<Answer score="0">Einstein</Answer>';
echo  '<Answer score="0">Bohr</Answer>';
echo  '<Answer score="1">Both</Answer>';
echo  '<Answer score="0">Neither</Answer>';
echo  '</Question>';
php?>

Usually multiple single-line echo statements are better expressed as a single multiline echo, but there is a length limit that is exceeded by the foregoing code.

PHP
echo  '<?xml version="1.0" encoding="UTF-8"?>
       <Quiz>
       <Question>
       Einstein or Bohr:  Who got the Nobel Prize for Physics?
       </Question>';

An even cleaner way to construct the file would be to remember that everything in PHP outside the tags is echoed by default and simply move all the straight XML outside the PHP tags.

PHP
<?php  header( "Content-Type: text/xml"); ?>
<?xml  version="1.0" encoding="UTF-8"      ?>
<Quiz>
<Question>Einstein or Bohr:  Who got the Nobel Prize for Physics?
    <Answer score="0">Einstein</Answer>
    <Answer score="0">Bohr</Answer>
    <Answer score="1">Both</Answer>
    <Answer score="0">Neither</Answer>
</Question>

Oops! We've turned our file inside out! Now it looks like an XML file with a single PHP processing instruction in it. It can be considered that way, as long as the .php file extension is left on it so that the server's PHP interpreter can find the code.

It is interesting that this file demonstrates the fact that the PHP tag is so similar to the XML declaration. Both are PI (processing instruction) codes in XML, with <?php targeting the PHP engine and <?xml targeting our XML processor.

Our data language has also matured. Note that there has been a healthy simplification of the quiz XML data structure. It now closely follows the format of the ActionScript object we recently created. This object design is terse and powerful. Question and Answer objects are defined by these constructors:

ActionScript
function Question( questiontext, answerarray ){
      this.q= questiontext;
      this.a= answerarray;
}
function Answer( text, score, comment ){
     this.text=text;
     this.score=score;
     this.comment=comment;
}

This leads to XML in the form of

XML
<Quiz>
<Question>question-text
    <Answer score="score-value">answer-text
        <Comment>comment-text</Comment></Answer>
      ..more answers (at least 2 in each question)
    </Question>
  ..optionally more Questions (at least 1 per Quiz)
</Quiz>

We can express this structure formally in a DTD:

DTD
QUIZ.DTD
<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT Comment (#PCDATA)>
<!ELEMENT Answer (#PCDATA, Comment?)>
<!ATTLIST Answer score (-1 | 0 | 0.5 | 1) #REQUIRED >
<!ELEMENT Question (#PCDATA, Answer, Answer+)>
<!ELEMENT Quiz (Question+)>

Examining it line by line we see

DTD
<!ELEMENT Comment (#PCDATA)>

A Comment is some text.

DTD
<!ELEMENT Answer (#PCDATA, Comment?)>

An Answer is also some text, with an optional Comment.

DTD
<!ATTLIST Answer score (-1 | 0 | 0.5 | 1) #REQUIRED >

An Answer must also have a score, which is either negative or positive 1, .5, or 0. These scores correspond to a set of predefined responses to the answer: wrong (–1), right (1), partial credit (.5) or do over (0):

DTD
<!ELEMENT Question (#PCDATA, Answer, Answer+)>

A Question is some text and two or more Answers.

DTD
<!ELEMENT Quiz (Question+)>

A Quiz has at least one Question, maybe more.

According to the definition, this Nobel Prize data is a legitimate Quiz, though a pretty skimpy one (Figure 10.1). It has no Comments and only one (easy) Question. But it is well-formed XML and tested against the Quiz DTD, it is valid XML, too. It doesn't have much entertainment value, but it is an adequate example for our experiments in PHP.

Figure 10.1. Simple Quiz in an XML-Ready Browser

PHP does more than simply echo text. PHP was created to make server scripting as easy as possible. Quite a lot of data is exposed to the script. For instance, it delivers an array of data that contains all the information about both the visitor's software and the host's server software. (The information about the remote browser was gleaned from the headers in the HTTP request that just arrived.)

$HTTP_SERVER_VARS is a predefined global variable in every PHP script. It is an associative array of variable names and their values. We can use an interesting feature of the PHP language to reliably scan through all the elements of associative arrays.

The keyword list() and the built-in function each() work together well. each() takes an array as an argument and returns a tiny array of two elements. These are the key and the value of the current element in that associative array. Every PHP array has an internal pointer and the concept of a current element. With each() the element is pointed to and then that pointer is incremented.

list() is a language keyword that looks and behaves like a function. Its arguments are a series of variables that you wish to treat as an array. Most typically list() is used to copy a known array into an ordered list of variables. That is what we will do here: We will copy simple array returned by each() as two variables. We know that this anonymous array has two members: the key and the value. So it is typical to list() them as variables like $key and $value. (For a more careful consideration of list() and each(), see the special sidebar on page 207.)

Before we can do anything with this data we need to tell the PHP script that the global variable exists. Even though it is predefined, it is not known to the PHP interpreter.

PHP
<html>
<head>  <title>Fifth PHP Code</title></head>
<body>
<?
global $HTTP_SERVER_VARS;
while (list ($key, $val) = each ($HTTP_SERVER_VARS))
    echo "<b>$key</b> => $val<br>";
?>
</body></html>

Note the HTML <b></b> tags, which set the key in boldface type, and the <br/> tag that sets line breaks. The tags help us digest the big bellyful of data we get back.

Output
DOCUMENT_ROOT => /home/webpages/userworld/htdocs/users/bigfun
HTTP_ACCEPT => image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
application/x-comet, */*
HTTP_ACCEPT_ENCODING => gzip, deflate
HTTP_ACCEPT_LANGUAGE => en-us
HTTP_HOST => bigfun.userworld.com:20775
HTTP_USER_AGENT => Mozilla/4.0 (compatible; MSIE 5.0; Windows 98;
DigExt)
PATH => /usr/bin:/bin:/usr/local/encap/ssh-
1.2.27.wsg.19991221/bin:/usr/X11/bin:/usr/andrew/bin:/usr/openwin/bin:/
usr/games:/usr/local/bin:/usr/bin/mh:~/bin
REMOTE_ADDR => 209.16.196.244
REMOTE_PORT => 4735
SCRIPT_FILENAME =>
/home/webpages/userworld/htdocs/users/bigfun/useractivepreviewtmp208.
php3
SERVER_ADMIN => webmaster@bigfun.userworld.com
SERVER_NAME => bigfun.userworld.com
SERVER_PORT => 20775
SERVER_SIGNATURE =>
Apache/1.3.6 Server at bigfun.userworld.com Port 20775
SERVER_SOFTWARE => Apache/1.3.6 (Unix) PHP/4.0.4pl1 mod_perl/1.21
GATEWAY_INTERFACE => CGI/1.1
SERVER_PROTOCOL => HTTP/1.0
REQUEST_METHOD => GET
QUERY_STRING =>
REQUEST_URI => /useractivepreviewtmp208.php3
SCRIPT_NAME => /useractivepreviewtmp208.php3
PATH_TRANSLATED =>
/home/webpages/userworld/htdocs/users/bigfun/useractivepreviewtmp208.
  php3
PHP_SELF => /useractivepreviewtmp208.php3
argv => Array
argc => 0

Let's use just one element from this cornucopia in our code. We will try to be worldly and serve a different quiz to Spanish speakers. We use the browser's automatic language request to try to deliver a localized quiz.

Part of the header in every HTTP request is the line

HTTP
Accept-Language: en-us

It defines the preferred language of the visitor (in this case, the American dialect of English). We look for the prefix "sp" that will match any variation of Spanish:

HTTP
Accept-Language: sp-ar

Since we are not looking for the priority value of Spanish in the Accept-Language declaration, we will catch cases where Spanish is listed as acceptable but not the first choice.

In fact, we will have cases where the first choice is English and Spanish is the second choice:

HTTP
Accept-Language: en-gb, en, fr;q=.8, sp;q=.1

This user wants English, preferably British English. Failing that, she will accept French which she understands eight times better than her minimal Spanish. By our simple test, she too is a Spanish speaker.

PHP
<?php
header( "Content-Type: text/xml");
echo '<?xml version="1.0" encoding="UTF-8"?> ';
echo '<Quiz>';
global  $HTTP_SERVER_VARS;
if( strstr( $HTTP_SERVER_VARS["HTTP_ACCEPT_LANGUAGE"], "sp" )) {
     echo '<Question>';
     echo   'Einstein o Bohr: &#191;Qui&#233;n recibi&#243; el premio
             Nobel?';
     echo   '<Answer score="0">Einstein</Answer>';
     echo   '<Answer score="0">Bohr</Answer>';
     echo   '<Answer score="1">Ambos</Answer>';
     echo   '<Answer score="0">Ninguno</Answer>';
     echo '</Question>';
     }
else {
     echo '<Question>';
     echo  'Einstein or Bohr:  Who got the Nobel Prize for Physics?';
     echo   '<Answer score="0">Einstein</Answer>';
     echo   '<Answer score="0">Bohr</Answer>';
     echo   '<Answer score="1">Both</Answer>';
     echo   '<Answer score="0">Neither</Answer>';
     echo   '</Question>';
    }
echo '      </Quiz>';
php?>

Note that all the element and attribute names are never translated. Flash is not set up to detect a <Pregunta>, only a <Question>.

We also see the use of numeric entity references in Spanish. For example &#191; produces the ¿ symbol used in Spanish as the start tag for a question (Figure 10.2).

Figure 10.2. Output as Seen by Browser with Spanish Preference
    I l@ve RuBoard


XML-Driven Flash Client

Let's return to our quiz game from Chapter 8 and upgrade it with the lessons from Chapter 9. Then it will be ready to interface with the PHP code we just wrote. We start with the original contents of Frame 1—a hardcoded database—and replace all the literal definitions of Questions and Answers with downloaded XML data.

ActionScript
function Question( questiontext, answerarray ){
      this.q= questiontext;
      this.a= answerarray;
}
function Answer( text, score, comment ){
    this.text=text;
      this.score=score;
      this.comment=comment;
}
whichquestion=1;
quiz =  new Array();

xml=new XML();
xml.trace = function()
{
     for( var i=0 ; i<this.childNodes.length; i++){
        this.childNodes[i].trace = this.trace;
        this.childNodes[i].trace();
        }

Unlike earlier versions of this recursive function, this one recurses first, then analyzes the current node. The advantage is that we discover the element as we are closing it, and all the data subelements it requires (Questions need Answers and Answers need Comments) have already been encountered and are ready to use.

ActionScript
if( this.nodeName eq "Comment" )
   newComment = getText(this);

A Comment simply grabs its text nodes.

ActionScript
if( this.nodeName eq "Answer" ){
   newScore= this.attributes["score"];
   if(!newAnswerArray)  newAnswerArray=new Array();
   newAnswerArray.push( new Answer( getText(this), newScore,
     newComment));
   delete( newComment) ;
}

An Answer assembles its own text, its score attribute, and any existing Comment. After making sure that there is an array into which it can place this Answer, it calls the Answer constructor with all the data and the stuffs the object produces into the array. It then destroys the Comment so that the next Answer starts with a blank.

ActionScript
     if( this.nodeName eq "Question" ){
        quiz.push( new Question(getText(this), newAnswerArray) );
        delete( newAnswerArray );
        }
}

Finally the Question object combines its own text with the array of Answers and calls the Question constructor. The Question is added to the Quiz.

ActionScript
function getText( node )
{
     text="";
     for( var i=0; i<node.childNodes.length; i++)
       if( node.childNodes[i].nodeType==3 )
          text+= node.childNodes[i].nodeValue;
     return text;
}
xml.cleanse= function ()

The body of this function (which cleans out white space) is omitted for brevity. It can be found in earlier chapters.

ActionScript
xml.onLoad = function ( ok )
{
    if( !ok )
       statusline = "XML Load failure";
       else{
           this.cleanse();
           this.trace();
           _root.nextFrame();
           }
}

Of interest is the last line of the onLoad handler. NextFrame() occurs when the load is succesful. However even though this code is written in the _root level and executes (like all object code) in the _root context, it does not suffice to call nextFrame() without specifying the context.

ActionScript
xml.load("quiz.php");
stop();

When it calls the PHP code, the result is lovely (Figure 10.3).

Figure 10.3. Quiz Data Downloaded from a PHP Server Script
    I l@ve RuBoard


Conclusion

We have made a real two-tier (client/server) application. Code that is running on the server in PHP is cooperating with ActionScript in the browser. Best of all they are communicating freely in XML! Our satisfaction is somewhat modified by the fact that we are still running out of hardcoded tables in memory. Real applications generally have powerful database back ends. And so shall we!


Chapter 11. Database Fundamentals

We have taught our Flash client to communicate with a server via XML. Our goal in doing this was to expose our Flash client to more data and allow the data to be dynamic. Housing data on the server is more dynamic than hardcoding it into our client, but it is still not optimal.

This chapter temporarily takes us away from both Flash and XML. We begin a study of the third tier of a three-tier web design, a database back end. We examine the fundamental principles and methods for using a relational database. Later chapters expand on this knowledge, allowing us to create a database of questions that we then relay back to the Flash client.


Background

We have written a program that serves up one of twenty-four questions. The questions are written directly in the source code file. But what if we want to store an unlimited number of questions? What if we would like to let people add questions without having access to our source code? Maybe we even want to let players submit questions.

We need to store our questions in an external file of some kind. We could store them in a simple text file and draw them out as needed, but there are some major problems with this approach:

  • The application must scan through and parse a lot of text to extract a particular question.

  • It requires a lot of bandwidth. We have to transfer the entire file and then dissect it. These problems tend to be exacerbated as the number of questions increases.

  • As the number of simultaneous users increases, problems of contention for bandwidth and file access occur.

  • We need to write the functions that read in and then parse the flat file. That is not fun.

  • We have to debug the functions we write. Even then, errors may occur in large-scale use that we could never simulate in a test environment. Perhaps the application crashes when the 65537th element is added because someplace we calculated an index using a 2-byte variable.


Advantages of a DBMS

A Database Management System can solve all the kinds of problems we just defined. A Database Management System (DBMS) is a product that provides a well-defined interface to read and write data to external files. Employing a DBMS yields many benefits.

The happiest benefit may be that we don't have to write low-level code. We don't have to parse text and we don't have to worry about outputting data to a file. All we have to do is learn a few commands, and then we can concentrate on the application code and let the DBMS manage the records.

A DBMS is carefully optimized to process commands very quickly. Unless we were willing to code our application in assembler (byte-by-byte machine code), it would be impossible for us to match the speed of a DBMS. While this may not be evident with the processing of simple commands, such as displaying all the objects in a particular portion of a table, it becomes apparent as the queries and the databases become more complex.

Another advantage of a DBMS is that we can streamline the data we get. For instance, instead of transferring all the questions and then counting how many were written by Joe Shmoe, we can have the DBMS transfer back only the number we are looking for. This saves us time because the DBMS is more efficient and also because fewer data must be transferred. The bandwidth problem is obviously critical when the DBMS and the program accessing it are on different machines, but it also occurs when the database and program accessing it are on the same machine.

NOTE

SCALABILITY AND INDEX

Scalability is the ability to handle increasingly large files and increasingly large number of users. These are critical concerns on the web. A DBMS scales better than a flat file technique: Doubling the size of a flat file may double the processing time of a request, but a DBMS processing time may increase by only a few percent.

DBMS scaling depends on the use of the index. An index is one way a DBMC organizes records. Indices are internal to the DBMS and they reduce retrieval times by orders of magnitude. However, each index also increases the update time, sometimes substantially.

There are many types of indices, and sophisticated database programmers can specify them for optimization of their applications. The details of optimization are beyond the scope of this book. Instead, we rely on the indexing performed automatically and invisibly by the DBMS itself.

The DBMS allows us to port our source over to other systems that have the same DBMS. We do not have to learn how to read/write files in UNIX, NT, or Sun whenever we wish to reuse our code on another project on another platform. Instead, we install the correct form of the DBMS, copy our code over, and we are set. Theoretically. In practice, of course, small differences between implementations require us to tweak the code.

Further, we can reuse the database without needing to invent tools to extract the data. Not only can we reuse the data in other programs we write, but we can also look at the table manually, via the console. A search through a flat file for all the players whose last name is Smith would be a lot more difficult, time-consuming, and error-prone than to type in

Select * from Players where last_name = "Smith";

and get a near-instantaneous response (slightly longer if there is an enormous number of names).


Advantages of SQL

The DBMS we use in this book is SQL, the Structured Query Language (the acronym may be pronounced either "sequel" or "ess-queue-ell"; both sides are bitterly adamant about their pronunciation, and whichever way you say "SQL," someone will correct your pronunciation). The version of SQL that we use is MySQL, and its creators specifically ask that it be pronounced "my-ess-queue-ell."

SQL is an easy language to learn. The queries seem like English, albeit broken English. Most commands are obvious, such as select, group by, and where. The next chapter explores this syntax. The popularity of SQL ensures that our investment in learning it will not be wasted and that plenty of resources are available to support our efforts.

Another benefit of SQL is that is an open standard, and there are several open-source ver sions available. Although this does not mean that all versions of SQL are free, it does mean that some are. Besides the monetary advantage, open source means that many specialized functions may have been added to the version of SQL you use (for example, a hypothetical command like REPLACE). It is important to note that if we use any special commands, we must install that version of SQL when we transfer our code to another server, and we lose many of the benefits of standardization.

SQL is a standard. It has a set of commands, defined by ANSI, that all versions of SQL should obey. We can reuse our SQL knowledge on other projects in many deveopment environments. Most languages (C++, Perl, ASP, PHP, Visual Basic, and so on) provide SQL support. They all have functions (or libraries with functions) that allow us to interface easily to the SQL database.

SQL is a speedy language. It is generally faster than the alternatives, such as OBDC, an object-oriented database programming system. Many require several stages of translation between the underlying database model, the access language, and the application. SQL is more direct.

Perhaps best of all, SQL is more than a DBMS. It is an RDBMS, a Relational DataBase Man agement System. Relational describes how the database is organized. Before we can really get into the relational aspect of SQL, we have to look at the parts of a database. What is there to know about a database? A lot. We might think of a database as a spreadsheet (e.g., Excel), but we'd be wrong.

In reality, a database is a collection of tables. Each table is what we typically think of when we imagine a simple spreadsheet. It has both rows and columns. A row is a record. It is a single instance of whatever the table is trying to store. For instance, our Quiz database will have a table of Questions. In the table, a single row would correspond to a single question.

Values

Each column is a unique data element within the record. For instance, our table of questions might have a column for the actual question text, a separate column for the name of its author, and yet another column containing the time each question was added. Each column stores one type of value. It may hold an integer, a floating-point number, a string, a date/time, or a binary file.

Parameters

In addition, each column has several parameters. One is the NULL setting, which determines whether or not the value NULL is permitted in case the value of that column is unknown or nonexistent.

Another parameter is the constraint. A constraint parameter produces an error message when trying to add a new row unless the value in the column is within the constraint. Suppose AOL pays us a lot of money so that only its subscribers can play our game. (We can dream!) We could add the constraint to the player's email-address column that its contents must include "@aol.com". Or, less fancifully, a gender column might accept only "M" and "F". This allows us to put checks on the input to a table.

There is also an AUTO_INCREMENT parameter. It enables this function: When a row assigns a NULL value to the column, the value is changed to that of the highest value in the column plus 1. Obviously, AUTO_INCREMENT works only on numerical values, and it functions only when the application supplies a NULL value to the column.

Similar to the AUTO_INCREMENT parameter is the UNIQUE parameter. (They are often used in conjunction.) UNIQUE ensures that no two rows in a column have the same value. It can work on numeric or any other type of data. It is especially useful when creating keys.


Keys

Keys are a kind of column. The most significant is the primary key. The primary key is the cornerstone of relational databases. Basically, the primary key is a column or set of columns that are unique in every row. (In the worst case, the primary key might consist of every column in the table.) The primary key allows us to positively identify a row. This is generally useful and absolutely critical when we wish to have tables of related information (a relational database).

For instance, suppose that our Quiz game database includes a table of questions and a table of players. We want one column of the Questions table to allow us to determine which player is the author of a question. We have a couple of options. We could use the player's e-mail address, but multiple players, say from the same family, may share an e-mail address. We could use the player's gamename, but we want to allow players to change gamenames and don't want to update our entire database whenever that happens (although this process can be automated, as we will discover later).

Suppose we assign the player a unique identifying number (UIN) that remains constant. (It could be generated, as we have seen, with AUTO-INCREMENT.) We can name this the "PlaID" (player identifier) and declare it the primary key of the Players table, since it is the way other tables will refer to rows within the table.

The gamename can be considered an alternate key. Like the primary key, it must have the property of being unique. In fact, merely identifying a column as UNIQUE allows the SQL engine to consider it an alternate key, and use it to reinforce and speed up indicing.

We've already seen a foreign key, although we may not have realized it. A foreign key is the column in a table that contains another table's primary key. So the column in the Question table that contains the PlaID number of the author is a foreign key. Declaring it as such gives us a few advantages. First, it will prevent us from entering a value in the column that is not a valid PlaID (a currently active ID in the Player table). We can't have authors that don't exist. Second, it allows us to dictate what will happen if there is a change to the row and column referenced by the foreign key. If the player has never written a question, of course, there is no problem. But if the player's PlaID appears in the Questions table, SQL will be careful about modifying or deleting it. (There are separate rules for each change.) In case of deletion, for example, the programmer will choose one of the following:

  • Prevent any change to values in use as foreign keys. (Players who have written active questions cannot be deleted.)

  • Set the foreign key value to NULL. (The question will have no author.)

  • "Cascade": When deleting the original row, delete every row that references it through out all the tables in the database. (When the player is erased, so are all the questions he contributed.)

A programmer may also choose from the same options for modification. "Cascade" changes the foreign key to match the new primary key value.

Joins

If we want to combine information from two (or more) tables, we could get the information from both and piece it together ourselves. However, there's a better way, called a join. We select which columns we will display from all the columns in two or more tables.

For instance, we expect a query to the Questions table to display the Author column in the form of the PlaID, a foreign key in the Questions table:

Questions
QuID | Text | Author
3228 | Who invented Chinese checkers? | 0012
0528 | Who wrote Howl? | 0178
1010 | Who invented Moore's Law? | 0012

The PlaID is the primary key in the Players table:

Players
PlaID | Name | Home
0012 | Barry | New York
0178 | Garry | Atlanta
0042 | Larry | DC

We can use join to replace the PlaID with actual player information:

Joined Players and Questions
QuID | Text | Name
3228 | Who invented Chinese checkers? | Barry
0528 | Who wrote Howl? | Larry
1010 | Who invented Moore's Law? | Barry

It is important to specify that you want to display only rows where the foreign key equals the primary key; otherwise, you end up with all possible permutations of the data. This can grow rapidly if you are joining several tables:

Unspecified Joined Players and Questions
QuID | Text | Name
3228 | Who invented Chinese checkers? | Barry
3228 | Who invented Chinese checkers? | Garry
3228 | Who invented Chinese checkers? | Larry
0528 | Who wrote Howl? | Barry
0528 | Who wrote Howl? | Garry
0528 | Who wrote Howl? | Larry
1010 | Who invented Moore's Law? | Barry
1010 | Who invented Moore's Law? | Garry
1010 | Who invented Moore's Law? | Larry

Note that the result is evil! It occurs when we forget to specify that the join should take place only where the Author column of the Questions table and the PlaID column of the Player table data are equal.

SQL performs several types of join, and they can be most easily distinguished with set theory. The two tables share entries in the joined columns. Typically this shared entry is a primary key in one table that is also the foreign key in another—the PlaID in the example.

We have been interested in seeing information only about the players who have submitted questions. That is, we are interested in players whose PlaID is in the Questions table and the Players table. This type of join is called an inner join. In particular, we are using a particular inner join operation that is so common that SQL identifies it as a special case. The natural join is an inner join that removes all the redundant columns. It is easy to see that this is a fundamental type of join.

Note that our table goes further. It also eliminates some nonredundant information, such as the city where each player is from. The method of eliminating columns is counterintuitive. Actually, in RDBMSs, we specify which columns we want; thus, if we want all the columns except the one showing where the players are from, we must list every column by name.

By creating a virtual copy of a table, we can have a table perform inner join with itself, a self-join. To be meaningful, of course, there must be a column that uses the table's own primary key as a foreign key. An example might be the database for a sleazy multilevel marketing scheme, where every seller has a distributor who also is a seller with a distributor, and so on.

Here we select the SellerID (primary key), Name, Territory, and DistributorID (self-referential foreign key) for the database of salespeople:

SellerID | Name | Territory | DistributorID
0014 | George | New York | 0002
3228 | John | New Jersey | 1010
0528 | Paul | New Jersey | 1014
1010 | Ringo | New Jersey | 0014

Here are the same columns after a self-join has been performed on the table and we select those sellers who work in New Jersey. We choose not to display the DistributorID column and instead add the Name column of the person whose SellerID equals the DistributorID. Note that we rename the Name column Distributor for the distributor's name, both to avoid two identically named columns and to make the table easier to understand.

Marketing Info after a Self-Join Associates a Name with DistributorID
SellerID | Name | Territory | Distributor
3228 | John | New Jersey | Ringo
0528 | Paul | New Jersey | George
1010 | Ringo | New Jersey | George

An outer join is similar to an inner join. The difference is that an outer join displays rows that have no corresponding values in the other table. For this reason, outer joins, unlike inner joins, aren't commutative. Two types of outer join exist, left outer joins and right outer joins (see Figure 11.1). The term left defines which of the two tables being joined has all its rows pres ent, the table to the left of the operator. For instance, if the players table were left outer joined with the questions table, we would see output like this:

Figure 11.1. Summary of Joins

Outer Join (Left) of Players and Questions
Name | QuID | Text
Barry | 3228 | Who invented Chinese checkers?
Barry | 1010 | Who invented Moore's Law?
Larry | 0528 | Who wrote Howl?
Garry | NULL | NULL

If there were any questions that had no author (suppose we had anonymous questions with an author code of 0000), they would not be displayed in a left outer join. If a right outer join were to take place, such anonymous questions would appear with a NULL author, but Garry's name would not appear at all.

NOTE

RELATIONAL DATABASE IN PRACTICE

We have a collection of questions, each of which has a set of answer choices.

How can we store them?

We could just include four answerchoice columns in the questions table. We'd be wasting some space when we only have three answer choices, but maybe space is cheap. It's easy enough to set an unused column to null. But what happens when we want to give a question five answers? It is easy to see that we might waste resources, limit our application, or both. But there's a better way.

Suppose we have a table with just answers. It might need only three columns, one for the answer text, one as a foreign key with the questionID number, and one indicating whether the answer is correct. Of course we won't have two identical answers to the same question, so the primary key of the answers table can be a combination of the two columns that contain the answer text and the question ID. Then when we get a question, we just look through the answer table and select all the answers whose associated ID match that of our question.

All these functions can be performed by the application, but the DBMS can execute them with great speed and simplicity by using the syntax we will learn in the next chapter.


Conclusion

Now we know the basis of RDBMSs. We still don't know how to tell any RDBMS to do anything, but that will be covered next chapter. The true test of programming skills is not syntax knowledge but a sound understanding of the concepts behind the code.

You always thought that database programming was just for geeks. Real geeks. Now that you see how it is done, you have to admit that it is less nerdy than you thought. (Good luck convincing anyone else!)


Chapter 12. SQL Syntax

Now that we understand how a relational database is built, we are ready to learn how to operate it. The interface to a relational database is SQL. We quickly become adept at building tables, populating them, and retrieving information from them.

We also become adept at designing tables. We learn a few rules to live by and are exposed to the various types of variables we can use to make tables.


Varieties of SQL

To some degree, syntax is standard among all of the SQL DBMSs. There are many DBMSs available; the most popular are those published by Microsoft and Oracle (Microsoft actually publishes two popular SQL DBMS offerings).

Flash techies seem to prefer open-source systems. For one thing, Microsoft and Oracle products cost money, while open-source systems are obviously free. For another, there is freedom to extend and debug the product and a community of sharing that benefits all. As in choosing PHP, we hope to benefit from and contribute to this community.

In the open-source world, the favorite DBMS is currently MySQL, and that is what we use in this book. MySQL has begun to cost money, which may later affect its popularity. But cost issues are not the dominant ones; syntax is more important. Most of the syntax in this chapter could be universally applied to any SQL program. But where the syntax varies from application to application, MySQL syntax will be used.


Prompt

We start typing MySQL code at a command prompt. First, from the OS we have to log into our database. Typically we start at a Unix prompt with a command like this:

UNIX
<unixprompt>: mysql—h  <mysql_socket>—u  <username>—p <password>

After we log in, the mysql> prompt appears (Figure 12.1). This is the basic command prompt where we will enter our MySQL commands. Much like C, C++, Java, JavaScript, and ActionScript, all commands in SQL end in a semicolon. Merely hitting the enter key results in the -> (line continuation) prompt. The -> prompt indicates that MySQL is still waiting for the rest of the line, terminated by a semicolon. (Text wrapping, however, does not cause the -> prompt to appear.)

Figure 12.1. The Command Prompt, Immediately after Logging In
    I l@ve RuBoard


Tables

One of the first things we learn to do in SQL is to create tables. It doesn't make any sense to try to store information or retrieve records if there is no table to keep the records in. Fortunately, the syntax is very simple. Unfortunately, the creation of tables is a discipline that, like all programming, comes partly from book learning but mostly from practice.

Creating Tables

First let's learn the syntax. SQL code looks like somewhat stilted English. To create a table, we write

MySQL
mysql> CREATE TABLE  <table_name> (
   ->  <first_column_name>  <datatype>  <special_flags>,
   ->  <second_column_name>  <datatype>  <special_flags>, ...
   -> PRIMARY_KEY (<first_column_in_key>,  <second_column>, ...)
   -> );

We then tell MySQL what we want it to do, namely, create a table. Then we follow with the name of the table we wish to create. The next step is where the most care is required. In parentheses, we list the name of the column, followed by the type of column, followed by any special flags, all separated by spaces. Commas separate each column definition.

Let's create a table of questions. After consulting with the designers of Quiz, we decide that a question should consist of the items shown in Table 12.1. The question has an unlimited number of answers, so putting them in the same table as Q (the text of the question) and the QuID (question unique identifier) doesn't make sense. If we remember what we discussed in the last chapter, we break the question definition into two tables, one solely of answers and one filled with all the other information.

Table 12.1. Planned Questions Table Organization
NAME | TYPE | MAX. SIZE | AVERAGE SIZE | KEY?
Q | String | 255 | 80 | No
PlaID | Int | Size of PlaID | N/A | Foreign (Players)
QuID | Int | 4.6 billion | N/A | Primary
DateCreated | Date&Time | N/A | N/A | No
Difficulty | Int | 255 | N/A | No
Genre | Set | N/A | N/A | No
A1 | String | 80 | 40 | No
A2 | String | 80 | 40 | No
An | String | 80 | 40 | No
IsTrue1 | Int | 1 | 0–1 | No
IsTrue2 | Int | 1 | 0–1 | No
IsTruen | Int | 1 | 0–1 | No

Questions table
Q PlaID QuID DateCreated Difficulty Genre

Answers table
QuID A IsTrue

In the two tables, the names mean the same and have the same properties as Table 12.1 spells out. Since no one wants to actually think of genres that make up the set right now, we just leave out the genre column and add it later.

New Questions table
Q PlaID QuID DateCreated Difficulty

Take a few minutes to see if you can create the code necessary to create the Questions and Answers tables. Then move on to the following code.

MySQL
mysql> CREATE TABLE Questions (
-> Q VARCHAR(255),
-> PlaID MEDIUMINT UNSIGNED,
-> QuID INT UNSIGNED NOT NULL AUTO_INCREMENT,
-> Created DATE,
-> Difficulty TINYINT,
-> PRIMARY KEY (QuID),
-> FOREIGN KEY (PlaID) REFERENCES Players
->     ON UPDATE RESTRICT
->     ON DELETE SET NULL
->     );
Query OK, 0 rows affected (0.08 sec)

mysql > CREATE TABLE Answers (
-> QuID MEDIUMINT NOT NULL,
-> A VARCHAR(80) NOT NULL,
-> IsTrue TINYINT NOT NULL DEFAULT 0,
-> PRIMARY KEY (QuID, A),
-> FOREIGN KEY (QuID) REFERENCES Questions
->      ON UPDATE RESTRICT
->      ON DELETE CASCADE
->       );
Query OK, 0 rows affected (0.00 sec)

As we can see, this code creates Questions and Answers tables. The two tables are linked by the QuID, which is a primary key in the Questions table and a foreign key in the Answers table that references the Questions.

Note that the primary key of the Questions table is a combination of QuID and the answer itself. It is obvious that QuIDs repeat and that multiple questions may have "Oort Cloud" as their answer. Okay, maybe not "Oort Cloud", but "Venus" certainly seems plausible. However, it should not be acceptable for "Venus" to be an answer multiple times for any given question. Ergo the primary key is a combination of the QuID and the answer's text.

NOTE

MYSQL FOREIGN KEY IMPLEMENTATION

Unfortunately these foreign key declarations are ignored by MySQL version 3.23. But, since other SQL implementations and future releases of MySQL may have this ability, foreign keys are included in the code. One implementation that currently is free and supports foreign keys is PostGreSQL.

Reviewing a Table Structure

Sometimes after we create a table we forget exactly how we set the table up. The solution to this problem is the describe keyword that follows this syntax:

MySQL
mysql> describe  <table_name>;

The describe command returns a table, with a row for each column, that looks like this:

Field | Type | Null | Key | Default | Extra
Column Name | Var. Type | Is Null OK? | Primary… ? | if null | Constraints

This function is helpful when we forget how we set up our tables or when we start work on someone else's system.

Let's use describe to review the Answers table we created earlier.

Output
mysql> DESCRIBE Answers;
+---------------+--------------+------+-----+---------+-------+
| Field         | Type         | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+-------+
| QuID          | mediumint(9) |      | PRI | 0       |       |
| AnswerChoice  | varchar(80)  |      | PRI |         |       |
| IsTrue        | tinyint(4)   |      |     | 0       |       |
+---------------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

Note that the foreign key (QuID) is signed. This may cause errors since the foreign key does not match the primary key of the Questions table. We need to alter the Answers table.

Altering Tables

We just created a table that holds the questions, and at the time of creation we forgot to set one of the columns to unsigned. Here's how we fix our mistake. First, let's look at the alter command. We can make four kinds of changes to a table (there are more, but they're unimportant). We can add a column. We can also modify a column. In both cases the code looks the same.

MySQL
mysql> ALTER TABLE  <Table_Name> [ADD,MODIFY]  <Column_Name>  <DataType>
<Flags>;

In addition, we can rename the column of a table.

MySQL
mysql> ALTER TABLE  <Table_Name> CHANGE  <Old_Column_Name>
<New_Column_Name>  <DataType>  <Flags>;

Last, we can drop a column. It is important to keep in mind that deleting a column permanently and irrevocably deletes all the data within the column.

MySQL
mysql> ALTER TABLE  <Table_Name> DROP  <Column_Name>;

We fix the Answers table as follows:

MySQL
mysql> ALTER TABLE Answers MODIFY QuID INT UNSIGNED;

Someday, when we forget what we called this table or whether Answers is capitalized or not (MySQL table names are case-sensitive), we will be thankful for the show tables command:

MySQL
mysql> SHOW TABLES;


Guidelines for Building a Table

The following suggestions make table creation easier.

  1. Always declare a primary key. The basis of relational databases is that each table has a way to refer to exactly one row. SQL can take care of this so that we don't have to. All we have to do is choose a column (or set of columns) to be our primary key. It makes deleting an entry easier and selecting a specific record faster. And it takes almost no effort on our part.

    There are a few exceptions, of course. You do not need a primary key, for example, if you plan to use the database as a very simple mailing list and will never refer to one particular column. Or you might not use a key if you add to a table orders of magnitude more often than you retrieve from it and retrieval time is less of an issue than update time (each primary key is an index and adds time to the update time). An example of an update-intensive application is a real time data collection application. It's better to create a key and not use it than need it and be unable to get to it.

  2. Blast information into "atoms." It is easy enough to concatenate a couple of pieces of information to form larger, more complex expressions, but breaking up text into smaller pieces involves parsing. We all know a few (very few) people who enjoy text parsing, but most of us don't. Let's assume that we want to avoid the necessity. Why would we want to store someone's entire name as one string when we can store a first name and a last name (and a middle initial too). It's easy enough to concatenate "Jesse" and "M" and "Jacobson" to make "Jesse M Jacobson," but we can also quickly make "Mr. Jacobson" or "Jesse" out of those fields. The smaller the data chunks, the more flexibility we have in how we use the data.

    Consider an algorithm that would correctly parse the names in Table 12.2.

Table 12.2. Parsing Names
Unparsed | Parsed
First | Middle | Last
Jake La Motta | Jake | La Motta
Sue Ann Nivens | Sue Ann | Nivens
Edna St. Vincent Millay | Edna | St. Vincent | Millay
Jesse M. Jacobson | Jesse | M. | Jacobson

  3. Avoid redundant columns. If we store the birthday of each of our players, there is no need to store their ages. Why? Because SQL always knows today's date and can quickly deduce age from birthday and vice versa. We delete the redundant column. How do we choose which redundant column to delete? We might chose arbitrarily. This is often the case. Or perhaps we decide that the data we store is used more often than the data we calculate. Or perhaps it's easier to calculate in one direction than another (like square root vs. square). But in our case, it's simple: A birthday is always the same, no matter what. A person's age changes constantly. So to avoid updates, we store just birthdate.

  4. Use the same data types in columns that must be compared. It's hard to compare an int to a char, and even harder to compare it to a float or a date. By hard, we mean time-consuming. Therefore, if you plan on doing a lot of comparison between columns, it's best if they have the same general type. Their size doesn't really matter, though.

  5. Be careful—it's easier if you don't have to change the table. The table can be changed after it's created. But by thinking through exactly what we want a table to do before we create it, we can save ourselves a lot of agony later on. Review the table, perhaps with a colleague, before using it. It's painless to change the table before we enter any data, but it's not easy with data already in the table. Modifying the table (except deleting or renaming columns) is tricky.

    For instance, suppose we enter a new column. All the entries already in the table would be assigned a null value for the column. This can create problems, especially if NULL is a meaningful state for that column. So we try to avoid it. But as the motto on a baby's bib goes, "Spit happens."


Types of Data

There are several types of data: integers, floating-point numbers, strings, temporal measurements, and binary objects. There are several types of each kind of variable, each one using a different amount of memory and storing a different range of values.

Integers

Integers are whole numbers. They have a range of 256Number_Of_Bytes and can be either unsigned or signed numbers. (In the latter case, the maximum value is half the unsigned maximum because the numbers are almost evenly split between positive and negative.) There are several types of INTs, each having a different size (Table 12.3).

Table 12.3. Different-Sized INTs
TYPE | BYTES | UNSIGNED | SIGNED
TINYINT | 1 | -128–127 | 0–255
SMALLINT | 2 | -32768–32767 | 0–65535
MEDIUMINT | 3 | -8388608–8388607 | 0–16777215
INT or INTEGER | 4 | -2147483648–2147483647 | 0–4294967295
BIGINT | 8 | -9223372036854775808–..5807 | 0–18446744073709551615

Floating-Point Numbers

Because floating-point numbers store digits in scientific notation, fractional values can be used. There are two kinds of floating-point numbers: one is twice as large as the other. A FLOAT uses 4 bytes. The other, called a DOUBLE, uses 8. A third type, DECIMAL, stores double data, but as a string rather than as a binary value. In some cases this wins speed at the cost of storage space (Table 12.4).

Table 12.4. Ranges and Sizes of Various Floats
TYPE | BYTES | RANGE
FLOAT | 4 | -1038 to -10-38 & 10-38 to 1038
DOUBLE, REAL | 8 | -10308 to -10-308 & 10-308 to 10308
DECIMAL, NUMERIC | (varies) | -10308 to -10-308 & 10-308 to 10308

Range is not the only difference between FLOAT and DOUBLE. They also have different degrees of precision. Not infrequently an application works well within the bounds of a FLOAT but uses DOUBLE for the accuracy it needs.

Strings

Strings are chains of characters. There are three kinds: CHAR, VARCHAR, and TEXT. CHAR and VARCHAR hold short strings. Unlike VARCHAR, every CHAR in a column has the same length. This wastes space but saves time in accessing the table.

Each TEXT holds as many characters as can be counted by an INT it is named after (Table 12.5).

Table 12.5. Different String Types
TYPE | BYTES | NOTES
CHAR | Variable (by column) up to 256 | Speed efficiency
VARCHAR | Variable (by string) up to 256 | Space efficiency
TEXT, TINYTEXT, MEDIUMTEXT, LONGTEXT | See Binary Objects

Temporal

Temporal objects include DATETIME (which locates any second within 10 millenia) and its components: DATE and TIME as well as the internally updated variable TIMESTAMP (Table 12.6).

Table 12.6. Temporal Ranges and Sizes
TYPE | BYTES | RANGE
DATETIME | 8 | 1000-01-01 00:00:00 to 9999–12–31 23:59:59
DATE | 4 | 1000–01–01 to 9999–12–31
TIME | 3 | -838:59:59 to 838:59:59
TIMESTAMP | 12 or 14 | Current time (read-only)

Binary Objects

BLOBs are large binary files that can hold literally anything digital: pictures, sounds, machine code, and so on. The TEXT variables in MySQL version 3.23 are implemented as BLOBs (Table 12.7).

Table 12.7. BLOB Sizes
TYPE | BYTES
TINYBLOB | up to 256
BLOB | up to 64K
MEDIUMBLOB | up to 16Meg
LONGBLOB | up to 4Gig

Sets and Enums

An ENUM contains a string. It must be one of the symbols defined in the ENUM declaration (e.g., an ENUM called gender might have "Female" and "Male"). A SET is similar but each record can have zero, one, or more of the named symbols (Table 12.8).

Table 12.8. ENUMs and SETs
TYPE | BYTES | RANGE
ENUM | 1 or 2 | Up to 64K different ENUMS
SET | 1, 2, 3 or 4 | Up to 64 individual flags


Adding Data to Tables

We created an empty table earlier in this chapter. But no matter how well defined, an empty table isn't very useful. We can add whatever records we want to our table using the insert into command. The syntax is

MySQL
mysql> INSERT INTO  <Table> VALUES (value1, value2...);

We can also specify the order of columns between the table name and values statements. We can even leave out columns (and their associated values), if they are allowed to be null.

MySQL
mysql> INSERT INTO  <Table> (col2, col1...) VALUES (value2, value1...);

Update

This command allows us to change the value of data within any table.

MySQL
UPDATE  <table> SET  <column> =  <value>;

SET causes all the records to have their <column>'s contents changed to <value>. This command is clearly much more useful when combined with the WHERE constraint. If WHERE identifies the primary key, we can update some or all of the columns in that record. Alternatively, we can do massive search-and-replace operations when WHERE identifies a value in the same target column as SET.

Example:

MySQL
mysql> UPDATE Questions SET Author = 28 WHERE Author = 12;

This command transfers all the questions that had been credited to Player 12 to the authorship of Player 28.

Delete

It is easy to remove a row from any table.

MySQL
DELETE FROM  <table>;

Clearly we want to use WHERE here as well. Without any constraints, we simply erase all the contents of the table!

Example:

MySQL
mysql> DELETE FROM Players WHERE PlaID= 12;

This removes Player 12 from the database. Some SQL databases can be set so that this deletion will automatically update all the tables that might refer to Player 12. For example, Players.PlaID is a foreign key in the Author column of the Questions table. The DBMS can remove all Questions linked to Player 12 or set them to Null Author, or it can prevent the deletion unless we perform the UPDATE that we did a page or so back, where we changed ownership of all of Player 12s questions. However, MySQL does none of this automatically, and it is incumbent on the programmer to remember to keep the references uncorrupted.


Reading Records from the Table

Of course, storing data is useless unless we can recover it. MySQL gives us a powerful assortment of tools to retrieve and organize data.

Select

When we want to query the database and retrieve data from the tables (or collect data from anywhere else, for that matter), we do so with a SELECT statement. The simplest syntax is

MySQL
mysql> SELECT  <columns_of_output>;

We can use this very simple form to get MySQL system information. So, for instance,

MySQL
mysql> SELECT now(), version();

yields a two-column result where the current time and date as well as version information are displayed.

Output
+---------------------+-----------+
| now()               | version() |
+---------------------+-----------+
| 2000-09-17 08:00:15 | 3.22.23b  |
+---------------------+-----------+

From

SELECT is most useful, of course, when querying the database. This requires us to identify the tables from which we are selecting data. To retrieve data from a table, we say

MySQL
SELECT  <* or columns of output> FROM  <table>;

Example:

MySQL
mysql> SELECT Q FROM Questions;

returns the text (column Q) of each entry in the Question database (Figure 12.2).

Figure 12.2. Using the Editor to Select the Q Column from Our Questions Table

Where

We generally want to apply constraints to the data returned from SELECT. The most straightforward contraint is the WHERE clause, which tests each row against a conditional to see whether or not it will be filtered out. Conditionals use familiar boolean comparisons (listed in Table 12.9) and logical connectors to test column contents against constants, other columns, or SQL functions.

Table 12.9. Conditional Operators
= equals
<=> NULL safe equals
<> != not equals
<[=] Less than [or equal]
>[=] Greater than [or equal]
IS [NOT] NULL | ISNULL(expr) | test for NULL
BETWEEN min AND max | within bounds?
expr [NOT] IN (value,...) | member of set?
INTERVAL(N,N1,N2,N3,...) | in ascending order?
[NOT] LIKE | SQL wildcard pattern match:
% matches anything or nothing
_ matches any single character
[NOT] REGEXP | [NOT] RLIKE | full regular expression test
STRCMP(expr1,expr2) | Zero when strings match

MySQL
SELECT  <data> FROM  <table> WHERE  <conditional>;

Example:

MySQL
mysql> SELECT Q FROM Questions WHERE Author=1 ;

returns the text of all questions written by Player 1.

Group By

Often it is useful to collect records in groups that share a common property. This feature combines all records with equal values in the same column.

MySQL
SELECT  <data> FROM  <table> GROUP BY  <column(s)>;

Example:

MySQL
mysql> SELECT Author, count(*) FROM Questions GROUP BY Author ;

returns each author and a count of the group of questions that he has written.

Having

The WHERE command balks at conditionals that have functions in them. WHERE would be unable to meet our needs if we decide to restrict the records returned based on the count() function in the previous example, so that only authors with more than 3 questions were displayed. HAVING fills this void. HAVING can use columns that are constructed using GROUP BY and that use functions such as count().

MySQL
mysql> SELECT Author, count(*) FROM Questions GROUP BY Author
    -> HAVING count(*)>= 2;

returns a table with all the authors who have submitted two or more questions.

Although in theory we could always use HAVING to replace WHERE, standard practice restricts the use of HAVING to those queries where its unique capabilities are required.

Order By

MySQL returns a table of data in the useless order of chronological creation. We can change this using the ORDER BY command, which takes as a parameter any number of columns. The table is then organized in alphabetic or numeric order (depending on the data stored in the column). After sorting based on the first column, ORDER BY uses the next parameter to break any ties, and so on until, after all parameters have been used, the creation order breaks any remaining ties.

MySQL
mysql> SELECT * FROM Answers ORDER BY WhichQuestion;

returns the entire Answers table, with all the answers to each question grouped together and the groups organized from lowest WhichQuestion to highest.

In case we wish to reverse the order, we can do so by adding the DESC keyword after the column name. The DESC keyword affects only the column it immediately follows.

MySQL
mysql> SELECT * FROM Answers ORDER BY WhichQuestion DESC, AnswerChoice;

returns all the answers sorted first in reverse order (highest to lowest) of WhichQuestion and second based on alphabetic order of AnswerChoices.

The sequence of these constraints in the SELECT query line is critical. They must be applied like this:

MySQL
mysql> SELECT  <data>
      ->        FROM <table>
      ->        WHERE <conditional>
      ->        GROUP BY <column<s)>
      ->        HAVING <conditional>
      ->        ORDER BY <column(s)>
      ->        ;

Alias

It is often useful to rename data as it is assembled by a query. The alias AS command allows us to create virtual columns and tables. This makes self-joins easy and also lets us smooth the transition from SQL database to XML datagram.

MySQL
SELECT  <data>  AS <alias>
SELECT  <data>  FROM <table> AS <alias>

Example:

MySQL
mysql>  SELECT Author, COUNT(*) AS x
    ->   FROM Questions GROUP BY Author ORDER BY x;

This code collects each author's questions into a group, counts them, and then uses the count (called x) to sort the result and present a list of each author's questions in order of how prolific each author is. Note WHERE, unlike GROUP BY, HAVING and ORDER BY cannot refer to a column by its alias.

Joins

Joins—as explained in depth in the previous chapter—are useful tools for combining data from more than a single table into one virtual table. In a well-used relational database, the contents of an object are distributed over several tables, and it is usually necessary to fold the data together with a join. This necessity is particularly true with XML objects, whose tree-like structure does not map easily to the rows and columns of an individual table.

MySQL allows us two types of joins. We are allowed to use both the inner join and the outer join. However, our outer joins are limited to the use of the left outer join. This restriction should be no problem, since we learned last chapter that a right outer join can be turned into a left outer join by switching the order of the tables.

We will need to refer to various columns in order to refer to specific records and match keys. We can do this one of two ways. If the column name is unique across the tables, we can simply use the column name. Otherwise, we simply refer to it by the name of the table and the name of the column separated by a period (<TableName>.<ColumnName>). We cannot use the alias of a column. However, if we are performing a self-join and use an alias to represent a table, we must use only the alias to reference the table.

If we wish to perform an inner join, we are allowed to be creative. There are several commands we can use to accomplish the join. CROSS JOIN, STRAIGHT JOIN, JOIN or simply ',' all indicate an inner join. We use the WHERE command to specify the conditions for syncing up keys. The various formats are

MySQL
mysql> SELECT Q, AnswerChoice FROM Questions JOIN Answers WHERE QuID =
    -> WhichQuestion;
mysql> SELECT Q, AnswerChoice FROM Questions CROSS JOIN Answers WHERE
       QuID = -> WhichQuestion;
mysql> SELECT Q, AnswerChoice FROM Questions STRAIGHT JOIN Answers WHERE
       QuID = -> WhichQuestion;
mysql> SELECT Q, AnswerChoice FROM Questions, Answers WHERE QuID =
    -> WhichQuestion;

In order to add the power to specify rows, we can add more conditions to the WHERE clause using an AND. So, if we wish to join the Questions and Answers tables, but are only concerned about a question with a QuID of 1, the command and output would look just like they do in Figure 12.3.

Figure 12.3. Screenshot of a JOIN in MySQL

If, on the other hand, we wish to perform an outer join (a left outer join, since that is all we are allowed), we are not given so many choices of syntax. The command simply is

MySQL
mysql> SELECT * FROM <TableA> LEFT JOIN <TableB> ON <TableA>.<Column> =
     -> <TableB>.<Column>;

A WHERE statement can follow the ON command to further specify rows.

MySQL
mysql> SELECT * FROM <TableA> LEFT JOIN <TableB> ON <TableA>.<Column> =
     -> <TableB>.<Column> WHERE <condition>;


Conclusion

With this material under our belts, we are ready to go out and build working database back ends. The ones we build for this book will be pretty simple. After all, SQL is only a sidetrack of our train of thought.

Now that we know SQL syntax, we will be better positioned to integrate SQL into PHP.


Chapter 13. Serving from SQL

Over the past few chapters we have learned why a DBMS is superior to a flat file. But so far our working system transmitted XML to the Flash client either from flat files or from hard-coded tables in the script.

In this chapter, we use PHP to access a MySQL database and dynamically generate the question that is then passed on to the Flash client. To do so, we first learn how to connect to the database. Then we use PHP to query the database. The information is returned to us in a table, which our PHP script must translate to XML. Finally, we send this XML to the Flash client. At the end of the chapter, we have this model in place: database to PHP to Flash (Figure 13.1).

Figure 13.1. The Flow of Data in Chapter 13
    I l@ve RuBoard


Connecting to a Database

We exclusively write PHP code in this chapter (although one of the PHP commands we learn can take MySQL code as a parameter). PHP has several functions that allow a script to connect to a MySQL server, so we must have access to one before we can play with this code. In addition to connecting to the MySQL database via a series of functions, PHP also has a collection of functions that act on the returned data. The results of queries are stored by PHP as variables, so once the query is actually collected, it is quick to go through. Starting with the code in Chapter 10, we have to replace only the constructor of the Question class. Then our PHP code will be serving questions out of a database.

Connecting to a Server

To use PHP to interface with a MySQL database, the first thing we have to do is to connect to the server that hosts that database. The standard command used to connect to a server is

PHP
MySQL_connect( URL, UserName, Password);

All three of these parameters are optional and each one is prohibited if any of the prior parameters has been omitted. The Password is assumed to be null. The UserName is assumed to be the same as that of the user who owns the process that is executing this command on the server. The default URL is the local host. In later versions of PHP it has become accept able to add to the URL either a port (as :port) or a path to a socket (as :/path/to/socket).

It is important to note that this function returns a value called a link identifier. Other than MySQL_connect, every MySQL command deals with an already existing connection or an already existing result.

We can open simultaneous connections to various servers. (If we connect twice to the same server, both connections return the same pointer.) When we have multiple connections, we must add a last parameter to every MySQL PHP query command. This is the link identifier for the selected server. Otherwise, the default is the last opened connection. Our simple project does not require multiple servers, so the optional end parameter is left off all com mands in our examples.

The connection remains open until the script that called it ends or we prematurely close the connection using the close command:

PHP
MySQL_close();

This closes the appropriate link. It does not affect persistent connections. Such connections can be easily created.

Persistent Connections

If you want to make a persistent connection that will not close either with this command or with the ending of the script, use

PHP
MySQL_pconnect(URL, UserName, Password);

This has some value if the same connection is opened by a script many many times, and it is more efficient merely to never close the connection.

NOTE

HANDLING CONNECTION ERRORS

Many PHP coders use a graceful style of error checking with lines like

MySQL_connect() or some_error_handler

It is conditional execution coded as a compound conditional expression. If the MySQL command returns nonzero (if it has not experienced an error), the rest of the or statement will be skipped. If there is an error, the handler is called.

Sometimes errors and warnings are undesirable. They can interrupt communications between client and server (or scare users.)

If we add the prefix @ to the connect command, the error messages generated will not display. Of course, it may help to know what the error we are catching is. MySQL_errno() returns the error number (or 0 if none) of the last MySQL_XXX command. MySQL_error() will return the text of the error message.

Server Commands

Most of the time we do not have the access required to add or delete (drop) databases. Nor do we desire to have PHP do this, when doing so by hand is much simpler and less prone to bugs.

PHP
MySQL_create_db(New_DB_Name);

This command creates a new database on the server that we are logged onto with the name New_DB_Name.

PHP
MySQL_drop_db(Old_DB_Name)

This command deletes a database.

A much more useful command is one that selects the database we want. For instance, over the course of this book, all our code has been stored in one database on one server. We've already learned how to connect to a server. To enter our database so that we can access all the tables within, we type

PHP
MySQL_db_select(DB_Name);


Interaction with a Database

PHP has gobs of useful commands and methods for MySQL. We can string them together to amend, update, and retrieve specific information from database tables. To apply our knowledge of MySQL, the most useful command is the query command:

PHP
MySQL_query(Line_Of_MySQL_Code);

Any single line of MySQL code can be inserted into this command (leave off the semicolon).

If the statement executes correctly, the statement returns a pointer to the resultant. The resultant is a small table that contains MySQL's response to our query. Unlike the database tables, which are located on another server in an unknown format, the resultant table is a PHP table, immediately accessible in local memory.

The pointer to the resultant should then be stored in a variable, which we can then use as an argument for supplemental functions. We then must use individual access functions to isolate actual data:

PHP
$SQLResult = MySQL_query( select * from Players);

Once we have executed the line(s) of code, we may wish to dissect the result(s) of our database access(es) or see how many rows our UPDATE command affected. That is why many different commands were invented that have PHP parse the returning data into an easily usable format. $SQLResult is difficult for us to use without applying these parsing tools.

In each of these functions, the first parameter is a pointer to the resultant table.


Parsing Commands

No more commands that return pointers are introduced in this chapter. The remainder of these functions do not access any SQL database at all, but rather rely on data already garnered from the database. These commands are devoted to processing the result of a MySQL_query command.

Now we cover fetch commands, so called because they fetch rows of the result, one at a time. These commands move through the result, which we pointed to, and retrieve the atoms of data that we have blasted our data into an easily predictable formula. The result object keeps track of the current row within the results data. By keeping a pointer to the next row to be fetched within the object, PHP assures us that every time we call the fetch commands, we get new data.


Fetching a Row

We can fetch a row three different ways. Essentially each way retrieves a single record, the one being pointed to, and returns it in a unique structure.

PHP
MySQL_fetch_row(Result);

This returns the data in a simple array. Each column's value is put in a separate element of the array. The array is zero-based. The range is 0 – Number_of_columns – 1.

PHP
MySQL_fetch_array(Result);

This returns an array. But this array is different from the array that we just fetched in the previous command. This array is an associative array. The key to access any value is the name of the column that the datum was stored in. In addition we can use the PHP array-accessing features (show, list, each) to move through this array, which, since a meaning is attached to each variable, can then be used easily.

PHP
MySQL_fetch_object(Result);

There is only one major difference between this command and the one preceding it. This one returns an object. Therefore, all the data is stored as a property with the name of the column being the name of the property. Also, unlike the associated array that was fetched before, this data cannot be accessed by referring to the offset of data. And there is no way to discover the column names if you do not already know them.

Although we did not create it and cannot directly access it, we can easily move the pointer to any row we choose. There are two functions we can use to do this. The MySQL_result command returns the value of any single field of the result. The Row is the number row of the result. The Column parameter is the zero-based column.

PHP
MySQL_result(Result, Row, Column);

This command moves the internal pointer within the result object to the row after Row. Any of the above commands that fetches a row affect the row immediately after the row that the retrieved field was in.

A second command to move the internal pointer expressly does solely that.

PHP
MySQL_data_seek(Result, NewRow);

This command moves the pointer to NewRow. Since all fetch commands retrieve rows, no column definition is neccessary.

Measuring Results

We can count how many rows were returned by MySQL_query.

PHP
MySQL_num_rows(Result);

This command returns the number of rows in the result—very useful information for setting up loop exit conditions.

Releasing Memory

Depending on how much information a result is storing and depending on how many results we have, we may start having memory problems. We can free up some of the results we are not using anymore for garbage collection. Garbage collection is when the program disassociates chunks of RAM with variable names so that RAM can be conserved.

PHP
MySQL_free_result(Result);

Elusive, erratic, and evil bugs may be the result of running out of memory. Fortunately, on today's modern machines, it is not as big a worry as used to be.

We have to be careful, because once a result is freed, it can never be accessed again. Once something is deleted it is gone, after all. To attempt to use the results will cause errors to occur.

The changes we make to the codebase in this chapter are based on the work of Chapter 10, where the following code appeared:

PHP
<?
class Question {
   var $idkey;
   var $Q;
   var $A= array();

Fakedata exists only until the MySQL database is working. It is, so it will disappear in our new code.

PHP
   var $fakedata= array (
             . . . lots and lots of hardcoded data . . .
   );

The "database" is queried during this object constructor, which is replaced later on in this chapter.

PHP
             function Question( $idkey ) {
                   if( !$this->fakedata[$idkey][Q])
                             return null;
                   $this->Q  = $this->fakedata[$idkey][Q];
                   while ( list($key, $choice)
                      = each($this->fakedata[$idkey][A] ) )
                             array_push( $this->A, $choice);
                   $this->idkey = $idkey;
                   return $this;
      }

This function creates all the necessary XML output. It remains unchanged.

PHP
function qzml(){
  if( !$beenhere++ ){
    echo  (   '<?xml version="1.0"?>'                   ."\ r\ n");
    echo  (      '<Question UIN = "'.$this->idkey.'"  >' ."\ r\ n");
    echo  (         '<Q>'.$this->Q. '</Q>'               ."\ r\ n");
    while ( list( $n, $answer ) = each($this->A) )
      echo(          '<A>'.$answer.'</A>'                 ."\ r\ n");
      echo  (     '</Question>'                           ."\ r\ n");
      }
   }
}
       srand(microtime()*1000000);     // seed random number generator
       $q= new Question( rand(0,5) );  // create a question
       $q->qzml();                     // display as xml
?>

Now we replace the object's constructor with one that queries our database. Most of it is straightforward code. A large conceptual problem, however, is choosing which question we want from the database.

There are several ways that we could use to decide which question we will withdraw from the Question table. One would be to plod through the questions sequentially, using a persistent variable to keep counting through all the QuIDs. We could even store this counter in its own MySQL table. If we wanted to be even fancier, we could keep a separate persistent variable for each of the users. But let's keep it simple and just select a random question regardless of past question history.


Trial-and-Error Approach

We will use the rand command to determine which question to send via XML to the client. We find the minimum and maximum QuIDs and use those two values as the boundaries for our rand command. But there may be invalid values in this range because some QuIDs were skipped in data entry or by later deletion of questions. So we could write code that follows the logic of Figure 13.2 and looks like this:

Figure 13.2. Guess and Check to Find Valid QuIDs

PHP
<?

class Question {
 var $idkey;
 var $Q;
 var $A= array();

The database is queried during this object constructor:

PHP
function Question( ) {
  MySQL_connect('sql.useractive.com', 'bigfun', '[REDACTED]');
  MySQL_select_db('bigfun');
  $min = MySQL_query ("select Min(QuID) from Questions" );
  $max = MySQL_query ("select Max(QuID) from Questions" );
  do {
    $WhichQuID = rand(MySQL_result($min,0,0),MySQL_result($max,0,0));
    $res = MySQL_query("select * from Questions where QuID = $WhichQuID");
     }      while(!MySQL_num_rows($res)) ;
  MySQL_free_result($max);
  MySQL_free_result($min);

   $sqlquestion = MySQL_fetch_array ($res );
   $sqlQuID     = $sqlquestion["QuID"];
   $this->Q     = $sqlquestion["Q"];
   $this->idkey = $sqlquestion["QuID"];
   MySQL_free_result($res);
   $res = MySQL_query ("select * from Answers where WhichQuestion =
          $sqlQuID");
   $numrows = MySQL_num_rows($res);
   for($i=0;$i<$numrows;$i++) {
      $temp = MySQL_fetch_array($res);
      array_push ($this->A , $temp["AnswerChoice"]);
      }
   MySQL_free_result($res);
   return $this;
}

There are several new functions in this code. The Min() and Max() functions return the minimum and maximum values in a column. These functions operate on a group, meaning that if the optional group by command is used, an individual sum (and so on) is computed for each. If there is no group by, then the entire result is treated as being in the same group. Other group functions in MySQL are Count(), Sum(), and Avg(). These functions compute the number of values, the sum of the values, and the average of the values, respectively.

PHP
function Question( ) {
  MySQL_connect('sql.useractive.com', 'bigfun', '[REDACTED]');
  MySQL_select_db('bigfun')

Connect to our server. Select our database.

PHP
  $min = MySQL_query ("select Min(QuID) from Questions" );
  $max = MySQL_query ("select Max(QuID) from Questions" );

Find out the maximum and minimum values for QuIDs in the entire Questions table. Store them in appropriate variables.

PHP
do{
   $WhichQuID = rand(MySQL_result($min,0,0),MySQL_result($max,0,0));
    res = MySQL_query("select * from Questions where QuID = $WhichQuID");
    }   while(!MySQL_num_rows($res));

Using the bounds we already understand, we randomly select a QuID. We then make the question object equivalent to the data stored in the record with that QuID.

We are not sure if the QuID we selected is real. We know QuIDs are unique, and we know the min and max—but we don't know if there are holes in the list. It is valid if there are. So we count the number of rows of output. If there are no rows of output, then the QuID is invalid—it is not associated with a question.

In this case we select and test another QuID—$res will have one row when we exit this while loop.

PHP
MySQL_free_result($max);
MySQL_free_result($min);

Do not forget to release unused memory.

PHP
$sqlquestion = MySQL_fetch_array ($res );
$sqlQuID = $sqlquestion["QuID"];
$this->Q = $sqlquestion["Q"];
$this->idkey= $sqlquestion["QuID"];

We take the data from our result. Then we store it in a couple of variables. $sqlQuID will be used later on. The last two declarations begin the actual construction of the object.

PHP
MySQL_free_result($res);

We free up some space.

PHP
$res = MySQL_query ("select * from Answers where WhichQuestion =
       $sqlQUIN");

We find the answers that are associated with the question we already have.

PHP
$numrows = MySQL_num_rows($res);

This line determines the number of times the following for loop will execute.

PHP
for($i=0;$i<$numrows;$i++) {
  $temp = MySQL_fetch_array($res);
  array_push ($this->A , $temp["AnswerChoice"]);
  }

This for loop moves through the result we received from the Answers database and adds the value in the AnswerChoice column to the question object's answer array.

PHP
return $this;

This code returns the Question we have just created for use by the preexisting PHP code.


Scalable Alternative

Another possibility is that we can just get all the questions and set the internal pointer to a random row. While it does require a large space for storing the result, this solution will work better if our QuIDs have a lot of holes, if questions are often deleted, or if they are entered in nonsequential order. The code necessary follows the logic in Figure 13.3 and looks like this:

Figure 13.3. Alternative Linear QuID Selection Method

PHP
//The only portion of this code that changed was the constructor

function Question( ) {
  MySQL_connect('sql.useractive.com', 'bigfun', '[REDACTED]');
  MySQL_select_db('bigfun');
  $res = MySQL_query ("select QuID from Questions" );
  $WhichRow = rand(0,MySQL_num_rows($res)-1);
  $WhichQuid = MySQL_result($res,$WhichRow,0);
  $res = MySQL_query ("select * from Questions where QuID = $WhichQuid" );
  $sqlquestion = MySQL_fetch_array ($res );
  $sqlQuid = $sqlquestion["QuID"];
  $this->Q = $sqlquestion["Q"];
  $this->idkey= $sqlquestion["QuID"];
  MySQL_free_result($res);
  $res = MySQL_query ("select * from Answers where WhichQuestion =
         $sqlQUid");
  $numrows = MySQL_num_rows($res);
  for($i=0;$i<$numrows;$i++) {
   $temp = MySQL_fetch_array($res);
   array_push ($this->A , $temp["AnswerChoice"]);
  }
return $this;
//     }

No loop, cleaner flowchart.

Only a little bit of the code was changed, the part that selected the QuID used.

PHP
$res = MySQL_query ("select QuID from Questions" );

Get all the QuIDs of all the questions. Load them all into RAM.

PHP
$WhichRow = rand(0,MySQL_num_rows($res)-1);

Choose a random row of data. The rows are contiguous and zero-based. There are MySQL_num_rows($res) of them. The bounds on rand are inclusive.

PHP
$WhichQuid = MySQL_result($res,$WhichRow,0);

This code selects a field; specifically, it selects the QuID in the randomly selected row.

PHP
$res = MySQL_query ("select * from Questions where QuID = $WhichQuid" );

This code retrieves the information about the Question whose QuID we have chosen. The rest of the code is the same between the two versions.


Conclusion

Now the PHP code we wrote in Chapter 10 truly does what it is supposed to. Congratulations. Our trivia game now has a Flash interface that reads questions from a database somewhere. That's really cool.

Our PHP script obtains questions from a database and sends them out in XML. In this chapter, we learned how to have PHP connect to a MySQL server, obtain results from the server, and then parse the results. Further, we extended our knowledge of MySQL a little by learning about some group commands.

We also went through two versions. This experience taught us to try to eliminate loops whenever for extra speed.

Commands Covered in This Chapter
Command name | What it returns or uses
MySQL_connect | Returns connection
MySQL_close | Uses connection
MySQL_pconnect | Returns connection
MySQL_create_db | Uses connection[1]
MySQL_drop_db | Uses connection[1]
MySQL_db_select | Uses connection[1]
MySQL_query | Uses connection;[1] returns result
MySQL_result | Uses result
MySQL_data_seek | Uses result
MySQL_fetch_row | Uses result
MySQL_fetch_array | Uses result
MySQL_fetch_object | Uses result
MySQL_num_rows | Uses result
MySQL_free_result | Uses (and loses) result

[1] Optional use


Chapter 14. XML Upload

In this chapter we learn to generate an XML file from within Flash and to transmit the generated file directly to a server process (a PHP script). We examine the XML data that arrives, first manually and then with increasingly sophisticated PHP functions. These operations require learning a series of critical skills:

  • Simple technique for assembling XML structures within Flash

  • Several functions that can be used to upload the XML to a server

  • Overview of content types

  • PHP techniques to capture an XML package

  • Browser display of XML and use of Data Islands

  • PHP techniques to transmit XML


Flash Client

As we explore these techniques, we build a login process, that can be used to register players as they join our game. We need a Flash front end where the player submits a username and password. The server receives this data and either accepts or rejects the player.

Text Features

The interface to our login process is quite simple. The player simply enters name and password and hits submit. The username is typed in plain text, but to prevent an over-the-shoulder security leak, the password must not be echoed. (In our game this is a matter of convention rather than a real issue.)

Flash offers developers an option on Input Text fields called Password (Figure 14.1). This is the easiest way to get the traditional star-echo in a secured input field. We generally prefer the easiest way of doing things. But in a game, we often want customized user interface details. For example, an input field might make a sound with each keystroke. We will make a quick little custom star-echo input field. This mini-exercise will expose us to the text controls in Flash and stroke-by-stroke processing with ActionScript.

Figure 14.1. Star-Echo the Easy Way

The labels on our two fields (the words "Username" and "Password") are simple static text with Selectable disabled (Figure 14.2).

Figure 14.2. Setting Static Text

The two actual input fields are of the type Input Text. They are tied to the appropriate vari ables username and password, which are local to this MovieClip. We enable the Border/Bg feature, which gives the player a simple clear input field, and we limit the allowed length of our players' names to 32 characters (Figure 14.3).

Figure 14.3. Setting Up the Username Input Field

There are a few differences between the password input field and the one for username. The password field has its text color set to Invisible so that it can be secretly entered. It also uses a monospace font (_typewriter) so that we always know exactly where the cursor will be (Figure 14.4). (We also limit the length of password to 16 characters.)

Figure 14.4. The Password Input Field

Finally we add another text field, superimposed over the password input field (Figure 14.5). This is of the type Dynamic Text and it prints a variable called stars. It uses the same point size and the same monospace font as the password field with which it is aligned (actually a slightly lower baseline looks slightly better; so does boldface). It is important that we do not enable the background or selectability. It should be visible but intangible as the player reaches through it to the input field below.

Figure 14.5. The Star Echo Field

The stars variable has a series of asterisks—as many as the characters in the password variable. ActionScript has minimal text manipulation functions and limited access to input buffer events, so to keep the two strings in sync, we repeatedly execute this code:

ActionScript
stars="";
for(var i=0; i<password.length; i++ )
    stars+="*";

We run this code every frame. Alternatively, we could create a new MovieClip object and attach our code to an instance of that object with a keystroke event handler.

ActionScript
 onClipEvent (keyDown) {  ..}

Submitting the XML

Now it remains only to add a submit button. We grab a generic pill button and attach ActionScript to call a local submit() function (Figure 14.6). By sensitizing it to both the mouse click (release) and the Enter keypress, we allow the player to hit Enter at any point to submit the login information.

Figure 14.6. Action of the Submit Button

Of course, we need to create the function submit(). This is where we experiment with different ways to communicate with middleware. We start with this simple test function.

ActionScript
function submit()
{
 login= "<LOGIN>"
           + "<USERNAME>" + username + "</USERNAME>"
           + "<PASSWORD>" + password + "</PASSWORD>"
       +"</LOGIN>";
  trace( login);
}

Now, running the ActionScript in Test Movie mode, we generate this fragment of XML as on-screen output.

Output
<LOGIN><USERNAME>Jimmy</USERNAME><PASSWORD>bluefish</PASSWORD></LOGIN>


Uploading to PHP

Now that we can formulate a simple XML string with the touch of a button, we are ready to send it to the server and interpret it there. There are several ways to do this.

Trying POST

Our first attempt to upload the XML stream employs the familiar POST method used by Flash and by HTML forms. Since the XML string is stored in the variable login, this seems easy. We will soon see that it is not.

First, we prepare a PHP routine that scans the incoming variables and prints each one out in variable-name => variable-value form. PHP delivers an associative array of all these variables to the script author.

NOTE

ASSOCIATIVE ARRAY

An associative array (also called a hash) is a list of values that can be in dexed not only by an ordinal number (i.e., myList[3]) but by a string (myList ["large"]). This string index value, often called the key, is used as a numeric index. For example, price["tulip"] might return a numeric value from an array of current prices indexed by flower types.

In PHP, even normal numerically indexed arrays are implemented as associative arrays, which decreases the speed of access but makes the arrays enormously flexible. There is no inefficiency in having two elements in array, one indexed as 0 and the other as 1001. Ordinary (scalar) array structure would require 1000 empty elements.

list() and each() allow the programmer to easily scan the contents of an associative array. The PHP keyword list() deconstructs a simple array by copying its elements into a supplied set of simple variables. List($a,$b,$c)=$myarray assigns to a, b, and c the values of the first three elements of the array. Each is a companion function that creates a small new array from each key/value pair of a target array. Each($myarray) returns a new array whose values are the first key and the first value of myarray. To make it convenient (but confusing), this temporary array is itself an associative array, and it has four elements: two copies of the original key and two copies of the original value. The keys 0 and "key" both return the original key. The keys 1 and "value" return the value.

The keyword each() also advances an internal pointer so that subsequent calls scan through the array. Good practice, then, suggests a call to reset($myarray) before any list/each loop. (The function reset() zeroes this internal pointer.) However, many of the listings here omit unnecessary resets when they would distract readers from more relevant portions of the code.

The variables exist in an array called HTTP_POST_VARS. As we saw in Chapter 10, PHP function must declare any global variable before it can access the variable:

PHP
global $somevar;

Alternatively, that value is also available as a member of the $GLOBALS array. (Be cautious: GLOBALS actually contains copies of the data; they can be read but not meaningfully rewritten.) This code fragment applies each() and list() to the array HTTP_POST_VARS within the standard PHP array GLOBALS. This exposes each of the variables sent by the ActionScript.

PHP
<HTML>
   <HEAD><TITLE>Testing XML</TITLE></HEAD>
   <BODY>
  here are the post variables<br/>
<?
  while(list($key,$val) = each ($GLOBALS["HTTP_POST_VARS"]))
    echo "$key => $val<br>";
?>
  <br/>
  That is all.
   </BODY>
</HTML>

We save this on our server under the name authorize.php.

Now let's turn our attention to the client side.The submit() function within our login actionscript must be updated slightly:

ActionScript
function submit()
{   login= "<LOGIN>"
           + "<USERNAME>" + username + "</USERNAME>"
           + "<PASSWORD>" + password + "</PASSWORD>"
       +"</LOGIN>";
  getURL ( "authorize.php","_blank", "POST" );
}

This Flash command tells the player to open a new window (_blank) in which to display authorize.php served from the same subdomain as the swf file.

The POST parameter tells the player to include all the variables located on this MovieClip instance (but not those on parent or child MovieClips).

We publish the swf file on our server and run it. The result (Figure 14.7) is a bit disappointing. ActionScript transmitted all the variables on this timeline, including pointless iterators and function names. The clutter of five unwanted variable names is not optimal, but they do no harm; the waste of bandwidth is not very significant in this case. Other situations might send very large amounts of noise data and introduce terrible inefficiencies. (Imagine a MovieClip that receives a large data load and sends back an acknowledgment. It is easy to imagine the acknowledgment accidentally coded so that it retransmits the entire data stream back to the server.)

Figure 14.7. Trace Results from POST Attempt

If recognized, these situations can be avoided by building a hermetic upload MovieClip and maintaining a pristine namespace containing only the upload variable. Such coding can be difficult. And the problem that necessitates this effort is often unrecognized, because the transmission occurs automatically and invisibly.

XML send()

The preferred solution, of course is to specify exactly what data we wish to upload. The getURL(..,.., "POST") function does not permit this. (Nor does the GET variation.) On the other hand the XML send() function provides us exquisite control over what data we present to the server. This is one of the many advantages we enjoy with XML data communication.

But first we complete the transmission of the XML string using traditional POST functions. It is important to learn this technique, even though it is regressive. Often sophisticated programmers find themselves in less sophisticated environments. It is not unusual at all to find oneself restricted to a server where the available middleware is not XML-ready (like PHP 3). The show must go on, and older technologies (like POST) can be called into play.

Returning to the output in Figure 14.7, we may be alarmed to note that the nice XML string we were sending as login has imploded. All the XML markup is gone, leaving only the naked text.

What happened? Did Flash strip out the XML markup before POSTing the string? Or maybe PHP filtered tags from the incoming stream when processing HTTP POST requests? Before we guess or jump to conclusions, let's inspect the HTML that the PHP page is sending. Using View Source in the browser window, we see

HTML
<HTML>>
   <HEAD><TITLE>Testing XML</TITLE></HEAD>
   <BODY>
  here are the post variables<br/>
submit => [type Function] <br>stars => ******** <br>i =>8<br>username =>
Mr. Happy<br>password => euphoria<br>login =>  <LOGIN><USERNAME>Mr.
Happy</USERNAME><PASSWORD>euphoria</PASSWORD></LOGIN><br><br/>
  That is all.
   </BODY>
</HTML>

The XML was actually sent from Flash to PHP properly, assigned to a string variable, and passed back from PHP to the client. It is the browser that stripped out the tags. If we think about this, of course, it makes sense. Although Internet Explorer 5 (the browser in use in Figure 14.7) and beyond can read XML Data Islands, they need to be properly identified as XML. The tags here look (to the browser) like unknown markup, presumably HTML.

Data Islands

We can delimit our code with the <XML> tag. (This is an HTML tag that identifies XML islands. It is not an XML tag.) The browser can read it as XML and treat it as data. In this case we inject the data into a simple HTML table.

The following PHP code extracts the POSTed variable login. It does so by selecting the $HTTP_POST_VARS array from the $GLOBALS array and then selecting from it the element keyed by login. The result is a string containing our XML data.

We wrap the string with <XML> tags and name it login. Attentive readers might realize that this login is quite distinct from the other login. In both ActionScript and PHP we have a vari able called login. In fact, the ActionScript login (both the name and the value parts) is passed directly to PHP. In both applications, it is a string variable that happens to contain an XML string. By contrast, the new login is coded in HTML and exists only in the browser (PHP sends the tags but knows no meaning). And within the browser's DOM, it is (for the first time) actual, parsed XML data.

To demonstrate briefly, we attach the login data object to a simple table using the table attribute datasrc. We then use datafld to extract records from it and populate cells of the table. This, of course, barely scratches the surface of how XML islands can be exploited in a very simple web page using only HTML and JavaScript.

PHP
<?
  while (list ($key, $val) = each ($GLOBALS["HTTP_POST_VARS"]))
    echo "$key => $val<br/>";
    echo '<XML id="login">' . $GLOBALS["HTTP_POST_VARS"]["login"] .
"<XML>" ;
    echo '<table border="1" datasrc="#login" >';
    echo '<tr><td>USERNAME</td><td>PASSWORD</td></tr>';
    echo '<tr><td><span datafld="USERNAME"></span></td>';
    echo     '<td><span datafld="PASSWORD"></span></td></tr></table>';
?>

The result is sweet (Figure 14.8). We have successfully slapped our ActionScript data into a handmade XML string and passed it up to the server and back down to a browser, where we displayed it as living data! But in truth, we took shortcuts at every step of the way. We built the XML using simple string concatenation instead of the XML constructors. We uploaded the XML using generic POST methods rather than those of the XML object. And in the end it is not on the server that the XML was parsed but in the browser. One by one, we upgrade each of these deficiencies.

Figure 14.8. Login Using POST

XML Constructor in Flash

To use the XML.send() function we need to have a legitimate XML object, so we upgrade our submit() function accordingly:

ActionScript
function submit()
{
 login= "<LOGIN>"
           + "<USERNAME>" + username + "</USERNAME>"
           + " <PASSWORD>" + password + "</PASSWORD>"
       +"</LOGIN>";
  xmlLogin = new XML(login);
  trace(    login            );
  trace( xmlLogin.toString() );
}

The trace results are not terribly exciting (Figure 14.9). It is difficult to say that the XML constructor and the XML.toString() method did anything at all since they produced the identical text we started with! Or we might see this as an affirmation of our hand-coded XML: It made the round trip in and out of the parser unchanged. To double-check, we add a bit of harmless sloppiness to the string—a couple of meaningless spaces in the tags. It proves that our string is really parsed into an XML data object, and then a new string is generated from the XML object. The parser ignores the spaces we put into our tags, and of course the generator makes fresh tags with no useless spaces (Figure 14.10).

Figure 14.9. XML Login String Before and After

Figure 14.10. XML Strings Before and After, Again

Now we replace getURL() with XML.send(). The functions are similar. Each addresses a window in which the results of the request are directed.

XML and HTTP Content-Types

Even though we are using this function to upload data to the server, we must continue to respect the conventions of HTTP. The server serves information. The client (browser) requests information from the server and waits to get a reply. Classically, the browser asks for a file (by specifying its URL), and the server delivers the file. This is how a page is viewed.

Sometimes the server needs more information to deliver the proper response. If the infor mation is modest and not very secret, it can be tacked onto the URL. (This is the GET method.) If the information is more bulky, the POST method is employed. In this technique, data is delivered in a block that follows the header as part of the request. This content is called the request body.

GET data, since it is appended to a URL, must follow the strict rules of a URL string as well as other rules that let it be distinguished from the resource locator portion of the URL. Only certain characters are permitted in a URL—in particular printable, simple ASCII characters. All other characters (such as space or ë) are disallowed. Then too, some characters have special meanings in a URL (such as @, . and : ), and they must be removed from the data. Finally, some characters have special meaning in the GET string itself (like &, ? and =), and these too must be replaced. Simple escape sequences substitute for disallowed characters. The resulting none none format is called x-www-form-urlencoded.

The POST method of uploading variables derived from GET in the early days of HTML. It uses the same encoding scheme. While other encodings (or as the protocols call them, other media, or even better, content-types) are technically permitted, this format is the default. Since many server and client software components support only this default, it becomes dangerous to use any other content-type and the default becomes ubiquitous.

XML is meant to be transmitted with different media. Its basic content-types are called text/xml or application/xml. Both are straight XML, without any of the escape sequences used by url encoding. Of course, XML also has its own set of allowable characters (and its own escape sequences). Compared to the rules of url encoding, the XML rules are far more permissive, more precisely defined, and more context sensitive.

The ubiquity of url encoding and the inflexibility of many software components (including PHP 3) created a confusing situation in which many systems were unclear (or incorrect) in the labeling and coding of XML data. Among the systems is Flash. Early versions (of 5.0) transmitted XML in url encoding under certain circumstances. (As of this writing, this is still true.) Behavior in the editor differs from swfs on the server. However, Macromedia was not carelessly in error; they were attempting to mate the output of Flash with the inconsistent implementations of various middleware packages. Much software still insists that XML arrive in the url encoded scheme.

The Macromedia solution is to leave it up to us. You need to define the content-type of your XML before transmitting it. Do not depend on the default, because the default is inconsistent! In local testing, expect text/xml, while on the server url encoded is the default. Early versions of the Flash 5 player were hardcoded incorrectly. (You might run into one of these unpatched originals; many millions were distributed. It is likely to jam up your XML site!)

The difference between application/xml and text/xml is not sharply defined or even agreed on. However, the fundamental difference is that text/xml is supposed to be human readable: It makes sense to nontechnical readers. On the other hand, application/xml is fit only for machines—and software engineers. PHP will read either one.

To create a true XML communication is easy now. On the ActionScript side, open the submit() function. Set the contentType property to text/XML and fire off a request to the same PHP file, targeting the same new window.

ActionScript
function submit()
{
 login=. . .
xmlLogin = new XML(login);
xmlLogin.contentType="text/xml";
xmlLogin.send(  "authorize.php","_blank");
}

Server Side XML Processing

Now we need to attend to the server side of this transaction. PHP processes HTTP requests before the script is invoked. Typical requests use POST or GET to send data to the script. As we have seen, this data is available to the script in predefined global arrays. The PHP has parsed the GET (or POST) string, peformed url decoding, and created all the requested variables and assigned them their specified values.

PHP 4 cannot interpret any other content types. If a block of data is received along with the request and it is not www-form-urlencoded, PHP simply leaves the data untranslated and supplies a pointer to it as the global variable HTTP_RAW_POST_DATA. So we can use this in our new PHP code.

PHP
<HTML>
   <HEAD><TITLE>Testing XML</TITLE></HEAD>
   <BODY> Here is the XML—from raw data <br/>
<?
    echo '<XML id="xmlpacket">' . $GLOBALS["HTTP_RAW_POST_DATA"]. "</XML>" ;
    echo '<br/><br/><br/><table border="1" datasrc="#xmlpacket"  >';
    etc. . .

We no longer use any variable name/value pairs but deal directly with the data (Figure 14.11).

Figure 14.11. The Server Echoes the XML without Processing It

Handling Raw XML

It is time we removed an unnecessary translation from our view of this data. We have always coded PHP to display its results on our browser using HTML. There are a few reasons that this shortcut might make us uncomfortable.

First, a real-world project rarely sends an XML data object to the server and then opens an HTML results window. Rather, we expect mature software to conduct two-way conversations entirely in XML exchanges. Therefore, we need to learn to send pure, well-formed XML from PHP.

Second, note that the HTML generated by PHP (in our authorize.php script) has become increasingly sophisticated. Now it uses not only straight HTML but an XML Data Island and invokes esoteric data linkage features of HTML 4. These techniques are certainly interesting—and very powerful and useful. But this direction takes us away from the main path of our development, the Flash/XML axis.

Finally, this technique does not allow us to freely examine the data. We print out the con tents of certain defined data fields. If the XML that we are receiving includes any other elements (or other instances of these same elements), we will never know, since we never asked the browser to display them. A complete display of all the XML will be more valuable for our purposes.

Fortunately we can easily browse pure XML if we are using Internet Explorer 5.0 or better. We can eliminate the clever table usage that depends on dataflds and datasrc. In fact, we can simply erase all the HTML and send simply the data island:

PHP
<?   echo "<XML>". $GLOBALS["HTTP_RAW_POST_DATA"]. "</XML>";        ?>

This code sends a legitimate XML island to the browser, which expected an HTML page. But nothing at all displays. This is because it is still indeed an HTML page with zero visible contents and an XML island that is still an island, even though it is 100 percent of the file.

Just as PHP required the data package to be clearly labeled as text/xml before it could be rec ognized, a similar problem occurs in the opposite direction. The server, as part of its HTTP response to an HTTP request, first sends a header. (Typically the header is followed by a requested file.) The header includes a "content-type" property that tells the browser how to interpret the bytes that follow. The default PHP content type is HTML, and it automatically puts in the header

HTTP
Content-type: text/html

The PHP function header() was written to override any of the statements in PHP's default response header. Note that the header() function must be called before any other response is issued. This not only means the explicit responses like

PHP
echo( "Some text");

but also implicit responses, such as any text outside the <$php delimiters. Most often the header function is torpedoed by a stray blank line before the first <$ tag. This whitespace becomes HTML output that precedes and frustrates the header command.

So the first characters in our PHP file are

PHP
  <?php
  header( "Content-type: application/xml" );
  echo $GLOBALS["HTTP_RAW_POST_DATA"];
?>

(Note that we have also dispensed with the <XML> </XML> tags with which we wrapped the raw POST data in our previous examples. That's because the earlier examples were using the data island feature to enclose a bubble of XML in an HTML page. Here the entire content is solid XML; it is exactly the XML fragment created in ActionScript, and labeled as such by PHP.) The two lines of code trigger the straightforward and interactive display of XML within the IE browser (Figure 14.12).

Figure 14.12. XML Viewed in a Browser
    I l@ve RuBoard


Conclusion

We are progressing splendidly. We have built a pathway for XML data from the client to the server and back. We have seen how transactions fit in the pattern of HTTP protocol, and we've seen the importance of proper content-type descriptors in the headers sent in each direction.

We have explored features and quirks of both Flash and PHP and have established solid communication between the two. We also practiced the display of the XML message in Internet Explorer—both as data tucked away as XML islands in an HTML page and as a pure XML file.

But if we are to be honest with ourselves, we note that PHP never really touches the data content of the XML it handles. So far, it merely takes a block of data that has been labeled as XML content and packages it properly to be recognized as XML when it is sent back to the browser in a new window.

In later chapters, we will parse this string into the structured data that it represents. We will also develop other points in the data drivetrain. We will generate the original Flash XML string more legitimately, and we will capture the XML response with Flash rather than a new browser window.


Chapter 15. Two-Way XML

This chapter extends the functionality we learned in the last chapter. We have uploaded an XML structure to the server in a stable and authentic manner, but we have not really read the file on the server side.

Now we introduce PHP's XML functions. We learn to distinguish event-driven parsers from the tree parsers we have already used, and we build an event-driven parser on the server. With it, we digest the data sent from Flash and formulate an appropriate response.

We develop the Flash client that interacts with this PHP script. The client delivers an XML data object to the server. The server parses the XML and responds. Flash receives and parses this XML response.


Event-Driven Parsing

PHP's core XML functions are built on expat, a seminal open-source XML parser published by Jim Clark. It is an event-driven parser, and it does an excellent job within its limited scope. It can unwind the syntax of an XML string, identifying each of its components and elaborating its external references. The parser does not validate XML, nor does it maintain any sense of state we would find useful.

We can use one of two distinct approaches to XML parsing. In Flash ActionScript we were able to depend on a DOM-based parser (sometimes called a tree parser). When this kind of parser is launched, it reads in the whole XML stream, parses the string entirely, and returns a single data object. The returned structure is a tree of well-defined node objects that precisely replicates the content and relationships of the XML formatted data. This static data structure is revealed all at once to the application.

Another DOM-based parser is the one built into Internet Explorer. It parsed our XML data island with no effort on our part, and we could access the results of the parser in a fully realized structure.

By contrast, an event-driven parser is continuously reacting to the different bits of data it encounters as the stream of XML runs through it (Figure 15.1). Unlike the DOM-based parser, this parser is in tight communication with the application throughout the parsing process. The parser and the application share the parsing process, with the parser identifying the points in time when something needs to be done and the application actually doing the something.

Figure 15.1. Event-Driven vs. DOM-Based Parsers

Event-based parsers have the following advantages.

  • They begin processing before reception is complete.

  • They begin response before processing is complete.

  • XML files can exceed memory size.

  • They can process unlimited XML streams.

  • They can be easily and acutely specialized for application.

  • They are faster than DOM-based parsers.

The advantages of DOM-based parsers follow.

  • They are easily invoked.

  • Parsing and application are completely distinct code bases.

  • The operation is automatic and invisible.

  • Data complexity and context are preserved.

It is worth noting that a tree parser can be built on an event-driven parser. (In fact, it is hard to imagine building a tree parser without underlying methods found in an event-driven parser.) PHP, in fact, will soon have a reliable tree parser written on top of expat. Several candidates are emerging from the clutter of complications and bugs with a reasonably full feature set. Of the three or four serious efforts, the most advanced is the DOMXML package, though it is considered difficult to install, incomplete, and incompatible with the DOM standard. Consider also the tiny but serviceable extension by Sebastian Will (Sol Folango) as a reliable alternative to a more complex parser.

Parsing with expat

Users of the expat library are required to write their own functions to handle the various parts of an XML document. These functions receive a set of parameters that are established in the expat spec. Special installers are called to set the event handler for the eight events that expat recognizes:

  • Start element This event reports the contents of the open element tag, which includes not only the element name but also an optional array of attributes as name/value pairs.

  • End element The closing of an element does not generate new data, but it is a critical event in managing contextual state. The application programmer is responsible for maintaining this state, and it can be fairly complex.

  • Character data This event is called when nonmarkup (text) material—usually the majority of the file and the most interesting part—is encountered. The event handler, not expat, funnels the data into the appropriate node and attaches it to the proper element. Without careful maintenance of context, the character data is almost sure to be worthless.

  • Processing instruction This event can initiate execution of code. It arrives with a target and data. If the target is "php" (the PHP processor), then the code can be run in line, with an instruction like eval($data). Note that ActionScript cannot be run like this, its eval() operator being incompletely implemented.

  • External entity This event occurs as the parser responds to the invocation of an external reference. It is the job of the handler to resolve the reference; the parser itself will ignore it. This handler must open the new resource and initiate a new parsing process with its own stream handling, its own buffers, and, in fact, its own parser. Since this external entity is free to invoke its own external entities (and so on), take care that the spawning of a new processor is reentrant and can execute recursively. The application is free to assemble a different parser for the referenced entity than for the original document. For example, the application's PI handler (e.g., eval()) might immediately execute processing instructions when they occur in the original XML file, although for the sake of security a more cautious approach would be wise.

  • Unparsed entity This event introduces (by reference) a non-XML file. As the name implies, this reference is not resolved by the parser and cannot (like the previous example) be digested by another instance of the parser. It is up to this handler to determine what to do with the reference. Although the reference can be to any binary resource, such as an application-specific data file, its anticipated use is for common media elements such as a JPEG image or an MP3 sound. (Modern practice seems to be moving away from this XML primitive in favor of the XLINK model of binary reference, as we will see shortly.)

  • Notation declaration The arrival of a notation declaration prepares the application for later occurrences of the unparsed entity event. Notation generally provides information on the nature of the non-XML files referred to and may even include application linkages.

  • Default If the event is not handled by any other handler, the default handler gets it. For example, there can be no special handlers for the XML declaration and document type. But the default handler is not limited to examples where expat does not have a special case. It also gets events that expat detects but for which the application has not supplied a handler. If an application (in the extreme case) only defines the default handler, then that handler is called with every tag, every string of text, and so on. (We will try exactly that.)

ActionScript Relevance

Most of the handlers for these events are unlikely to be used in interpreting a message sent from ActionScript. The start and end of elements and the arrival of character data are probably the only events of interest in this context.

In particular (as we noted), the processing instruction is language specific and cannot be used to send ActionScript to the client. In theory, it could be used by Flash to send instructions to execute on the server, but this is a very indirect way to get things done. More important, such a technique throws the front door wide open to the most pernicious security attacks.

The three events that process entities and notations are of little use in Flash development. They are far more sophisticated than is required for the simple client-server messaging generally employed. These three are guaranteed to be meaningful only on a validating parser.

Although the PHP parser is not validating, it can be made to resolve external entity invocations (including a DTD). It can do so, however, only after you write handlers that open new files and recursively invoke the parser. With enough effort on your part, the PHP parser will support these three XML features. The ActionScript parser will not, no matter what you do. It is a nonvalidating parser and a fairly minimal one. So implementing external entities will be meaningful only on the server side.

It is possible for a client and server to traffic in XML objects that only one of them can read. ActionScript can be forced to generate fully valid XML code (just as it could be forced to generate meaningful LISP instructions). Generally this means stupidly echoing static strings that the developer has prepared.

The purpose of XML is not served by parroting code that is understood only on one side. It is meant as a pathway in which data objects can be passed from one process to another while remaining lucid to both.

Real-World Relevance

The XML features ignored by ActionScript are not meaningless for the Flash developer. For example, in developing our game, we might choose to publish player profiles. A profile might include a photograph.

Our XML document must refer to external (non-XML) files. The proper XML route is to develop notation for GIF89 and JPEG file formats and create unparsed entity references that point to photographs on a server. With considerable pain, we can do this in ActionScript. We can write functions that clumsily extend its XML generator (not its parser). They could compose unparsed entity code and inject it into the XML string.

This correct XML can be quite useful. XSL transforms could be written to prettily embed the photographs in HTML player profile pages. PHP code could be created to manage the database. Even better, if a native XML database server is being used, it will hum happily when this data arrives.

The unparsed entity and the notation were specifically created for this sort of use. But they are at once delicate and clumsy. The specification, unlike the sparse elegance of most XML, is sprawling and nearly ambiguous, and it is devoid of any sense of design.

Proper XML photograph links require us to wade through half a dozen features that appear nowhere else in XML (such as the PublicID). The result is an XML object that would be meaningful only to the most sophisticated parsers. In particular, it would be unreadable by the Flash code that first wrote it. If the player profiles were to be eventually displayed by Flash, these exquisite XML objects would be useless.

NOTE

An increasingly popular alternative to this method of referring to external resources is XLINK. It provides a set of standardized link elements modeled after (and greatly extending) the HTML <A/> tag . Like the classic hypertext markup, XLINK relies on these defined elements and especially attributes (like href) to specify linkages, in contrast to strict XML, whose system of external entities uses a variety of features but not standard elements and attributes. ActionScript will process an XLINK document, but it will choke on strict XML linkages.

In the following example, ActionScript ignores the namespace reference on the second line and imperfectly resolves the use of that namespace. Rather than interpret xlink:href properly as "href as defined by the owner of www.w3.org/1999/xlink", Flash just interprets it as "some attribute called xlink:href". In practice, this makes very little difference.

<?xml version="1.0" encoding="UTF-8"?>
<player xmlns:xlink="http://www.w3.org/1999/xlink">
   <name>  Jimmy Commender    </name>
   <home>  Staten Island, NY  </home>
   <photo    xlink:type="simple"
             xlink:show="onLoad"
             xlink:href="hotcop.jpg"
             width="100" height="200"/>
   <score>42300 </score>
   <favorite-url xlink:type="simple"
             xlink:href="http://www.commender.com/~jimmy">
        My home page
        </favorite-url>
   <favorite-url xlink:type="simple"
             xlink:href="http://www.bigfun.net">
        My favorite game site
        </favorite-url>
</player>

This is a far more appropriate mechanism for external file references than the complexity of unparsed entities. The simple XLINK mechanism fits the simple ActionScript parser and the simple PHP implementation we prefer.

Since it uses only the element mechanism, not notation and unparsed entities, XLINK information passes easily through even the simplest XML parsers. It is easily preserved and easily repeated, even by parsers that are ignorant of XLINKs and incapable of using the data.

If we choose to create our own ActionScript linkage functions, the choice of XLINKs as a basis jumps us forward with a powerful and clearly specified design, a simple XML format and a community of support that offers tools, content, and much greater return on our development effort.

Relevance Decision

Let us limit the ambition of our PHP parsing: We want to simply match the capabilities of the ActionScript parser. Our little sigh of regret for the skipped features is overwhelmed by a big sigh of relief for the skipped work. We need to handle only element tags and text. If we ignore the exotica, we can concentrate on doing the elements correctly—on managing their context and their contents and their attribute bundles as we build a document tree.


Building the PHP Parser

XML parsing with PHP has five steps.

  1. Write functions to handle elements and text.

  2. Create a parser.

  3. Install the handlers.

  4. Obtain XML string.

  5. Parse.

Step 4 and 5 are repeated as needed for large XML files or continuous XML streams.

Step 1 is where all the thought and creativity is required. So let us demand little of our first parser: The handlers will be little more than stubs as we engage the rest of the process.

PHP
<?
function unknownXML($parser, $text) {
    echo "what is this? ( $text )<br/>";
}
$xml_parser = xml_parser_create();
xml_set_default_handler($xml_parser, "unknownXML");
$data= $GLOBALS["HTTP_RAW_POST_DATA"];
if (!xml_parse( $xml_parser, $data, true ))
     die("XML error: xml_error_string(xml_get_error_code($xml_parser))
             at line xml_get_current_line_number( $xml_parser ))" );
xml_parser_free($xml_parser);
?>

In this code we see a pretty straightforward implementation of the five steps. Note these details:

  • XML parser handlers are installed by referring to the name as a text string, not with the sort of function pointer we might expect.

  • The true parameter for xml_parse() marks this parse as the final one for this XML object. In a simple one-shot parse like this, it is not an interesting parameter. But remember that large XML files can be loaded in chunks, and they rely on this flag.

  • The parser is explicitly destroyed when complete.

Punching in a username and password in a Mardi Gras mood yields the screen in Figure 15.2.

Figure 15.2. Echoing the Calls to the Default Event Handler

Now let's look a bit closer. Let's install specialized handlers for opening and closing elements and for handling character data. We add these lines to the PHP script.

PHP
<?
$indent="";
function startElement($parser, $name, $attrs) {
    global $indent;
    echo "$indent $name<br/>";
    $indent.= "- - ";
}
function endElement($parser, $name) {
    global $indent;
    $indent= substr( $indent, 0, -4);
    echo "$indent $name ends<br/>";
}
function characterData($parser, $text) {
    global $indent;
    echo "$indent $text<br/>";
}
. . .
xml_set_element_handler(       $xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
. . .
?>

We get the nicely formatted output in Figure 15.3. This is fine for echoing to the screen the parse events as they occur, but it does not do anything interesting or useful with the data.

Figure 15.3. Echoing the Calls to an Extended Event Handler Suite

Our next step is to build a simple set of handlers. First we build a little parser that collects very simple messages. It simply builds a table of elements and their contents—a flat table of elements with a single entry for each. Therefore it has these limits:

  • No attributes. The following becomes just "iron: 9".

XML
<iron unit="tons" quality="pig" source="duluth">9</iron>

  • No context. It cannot distinguish between a putter and a trainload of ingots.

XML
<golf-clubs><iron>9</iron></golf-clubs>
<freight><iron>9</iron></freight>

  • Only one instance, the most recent, is saved. The following resolves to just a 6 iron.

XML
<golf-clubs>
    <iron>9</iron>
    <iron>7</iron>
    <iron>6</iron>
</golf-clubs>

  • No nodality within the text. The formatting in the following will disappear.

XML
<iron>Sunbeam EasyLife <bold>cordless</bold> model</iron>

But even with such odious limits, this simple parser is perfectly adequate in many situations where the Flash client is sending a few simple pieces of data to the server. Our example here, where only a simple username and password are transmitted, is typical of this sort of communication.

First we establish three variables. One, theElements, is an associative array in which the key strings are the names of the elements found and the values are the contents of the elements. We track the currently active element with a simple string variable, thisElement. And to support nesting of elements, we build a stack, called oldElements, of currently open elements.

PHP
<?
  $theElements=array();
  $oldElements=array();
  $thisElement="";

function startElement($parser, $name, $attribs ) {
   global $thisElement, $oldElements, $theElements;
   array_push( $oldElements, $thisElement);
   $thisElement=$name;
   $theElements[ $thisElement ] = "";
}
function endElement($parser, $name) {
   global $thisElement, $oldElements;
   $thisElement= array_pop( $oldElements);
}
function characterData($parser, $text) {
   global $thisElement, $theElements;
   $theElements[ $thisElement ] .= $text;
}
function unknownXML(  $parser, $stuff) {
    echo "What is this stuff? ($stuff)<br/>";
}

Note that the expat parser does not guarantee that all the character data for an element is sent in a single call to characterData(). It does not even promise that the calls will be broken on nodal boundaries. So our function features a concatenation rather than an assignment.

Also, empty elements are often very important. In some vocabularies they form significant tokens, application-specific keywords. The message might consist mostly of such tokens, as in the popular mobile-device protocol WAP. Or you might send a single such token: <STOP/> or <logoff/>. So this version of the parser creates such empty elements in the start_element handler.

Building and launching this parser is no different than creating the previous one. But when it comes time to display results, we need to scan through the array of elements. Let's display the results as follows (Figure 15.4).

Figure 15.4. Associating the Events into Full Elements

PHP
reset( $theElements );
while (list($elementName, $elementContent) = each( $theElements ) )
   echo "$elementName  => $elementContent <br/>";

Note that our structure has been flattened. Our wrapper element <LOGIN> is demoted from parent to sibling, and since it contains no text of its own, it is an empty element.

But why try to format a pretty response? Let's respond with functional XML.

PHP
header( "Content-type: text/xml");
echo "<RESPONSE>";
reset( $theElements );
while (list($elementName, $elementContent) = each( $theElements ) )
   echo "<$elementName>$elementContent</$elementName>";
echo "</RESPONSE>";

Having flattened <LOGIN> out of its position, we need to create a new wrapper element. (Remember that XML requires the entire document body to be one single element.) So we have created a <RESPONSE> object which contains the entire XML message (Figure 15.5). We can send XML to PHP and it can parse it—carefully or quickly. And the PHP scripts can output well-formed XML. But we do not yet have XML in-and-out from Flash. Although we performed XML input earlier and output recently, we have yet to send an XML message and capture the XML result.

Figure 15.5. XML Response

There are two XML upload methods. We have used the .send() method; now we will use the .sendAndLoad() method. The two parameters of send() are the URL to which it is being sent and the window in which the results are displayed; In sendAndLoad() the second parameter is the name of the XML object that will be the load target of the response XML.

Remember time. The script we are writing should last for only 100 milliseconds or less. And the time between one line and the next is measured in microseconds. By contrast, we can expect that it will take several seconds to transmit a data package to the server, have the server script parse the XML, act on it, and formulate a response. Even then the response needs to be transmitted back to Flash and parsed on the client side before the data becomes visible. Rather than loop around in a while() loop—unthinkable—or loop around the timeline—unneccessary—we can assemble the event-driven architecture of this functional component.

Later our password will unlock doors. For the moment, it will just display more XML—but this time entirely within Flash. We set a dynamic text field called statusline on our screen (Figure 15.6). We make a little method for our XML object called showme(), which uses the .toString method as a way to quickly visualize a small XML object like this.

Figure 15.6. The Creation of statusline

ActionScript
function showme( ok )
{
  statusline="Received: \n"+
              (ok?  this.toString() : "terrible load failure")
              +"\n("+this.contentType+")";
}

contentType Property

Open up the submit() function (which, you may recall, is fired when the submit button is pressed or Enter is hit). We will replace the .send() function. We need to create a fresh XML object, which we call xmlResponse. We register the showme method to xmlResponse as its onload action, which occurs when the load has completed or failed. (These two cases are distinguished by the ok flag that onload passes and which showme() observes. )

// xmlLogin.send(  "authorize.php","_blank");
  xmlResponse = new XML("");
  xmlResponse.onload = showme;
  xmlLogin.sendAndLoad( "authorize.php", xmlResponse );
  statusline="WAITING for XML";

The statusline assignment ("WAITING…") is displayed from the time the submit action starts (button pressed) until onload occurs (communication completed).

We have made our connection! We composed a message in XML and sent it to a PHP script, which read it and sent back a legitimate XML message that Flash captured neatly (Figure 15.7). But there are still shortcomings.

Figure 15.7. The statusline Shows the Server's Response

The response we receive is just a stepped-on version of the original XML message Let's make authorize.php do some work, not simply repeat what it hears. In particular, we should get it to authorize users. Ultimately, we will validate username/password pairs against a database back end. But before we explore the delights of the database, let's complete the round-trip communication between Flash and the server.

The special username "anonymous" allows open entry to our site. Its only restriction is that the visitor needs to know a public password (and be able to spell "anonymous" correctly). The public password can change frequently or never. Today the password is "bluefish." We allow ourselves a shortcut and hardcode the password into the PHP file. (Easy for us, hard for the maintenance webmaster. But to ameliorate this a bit we put the password in the first line of our PHP file.)

PHP
$anonymousPassword= "bluefish";

And we add the following to our XML output section.

PHP
if( $theElements[ "USERNAME" ]== "anonymous")
   if( $theElements[ "PASSWORD" ]== $anonymousPassword )
       echo('<ACCEPT privilege ="guest"/>');
   else
       echo('<DENY error="404">Incorrect anonymous password</DENY>');
else
   do_something_else();

Now when we type in "anonymous" and "whitefish," we get the screen in Figure 15.8.

Figure 15.8. Bad Password

On the other hand, if we type in "anonymous" and "bluefish" we get the screen in Figure 15.9.

Figure 15.9. Good Password

For now do_something_else() drops back to echoing XML. Even more fun happens later when it tests the names against the database, but first let's get the client-server link solid.

The best way to develop and (especially) debug a networking project like this is to use powerful tools. Examining the results of transactions after the host or the client has manipulated the data is often confusing. In the last chapter, for example, we found that the tags had disappeared from an XML string between the time we composed it and the time we examined it. We suspected both the Flash code that generated, packaged, and transmitted the string and the PHP code that received and possibly overprocessed it. In fact, it was neither. The tags were eliminated by the HTML interpreter, which was displaying the debug dump from PHP.

In that case we were able to discover the answer by inspecting the source. Here, we have an anomaly buried deeper in the protocol. Flash tells us that it has received a message in the application/x-www-form-urlencoded format, but we had told the PHP to send it as text/xml.

Since it seems to work, the disagreement is not very consequential, but its solution allows us to glimpse an important tool. Packet sniffers allow the programmer to learn exactly what traffic is occurring along a connection.

On this screen in Figure 15.10 we see the request segment of the HTTP transaction, in which the Flash client sent a header that (besides identifying the format and protocol of the request itself as well as the requesting browser and the intended server page) announces a 63-byte content of type text/xml, and that is exactly what follows. Which makes sense, since it is exactly what we asked Flash to transmit.

Figure 15.10. Packet from Client Request

If we look at the response with the packet sniffer, we see a wealth of interesting data (Figure 15.11). This is the packet we told PHP to send as Content-type: text/xml but which Flash (at least this version) identifies as application/x-www-form-urlencoded. Clearly the packet sniffer shows who's telling the truth. It is not only labeled text/xml in the header, the actual contents are in that format.

Figure 15.11. Packet from Server Response

Great programmers do not guess what is wrong (or guess what is right either). They test.


Conclusion

Using PHP, we have created a simple XML parser on the server. In doing so we went beyond the simple DOM parser and learned event-driven techniques. Our parser read in a simple XML file and generated a response.

On the client side, too, we are now able to both read and write XML. We can even use the XML.sendAndLoad() function, which exchanges the XML object in memory with the server. A two-way XML data exchange is functioning smoothly: Both sides can receive, alter, and retransmit an XML object. We can design all sorts of data-sharing strategies, both symmetrical and assymetrical.

We have also been introduced to a powerful tool, loved by hackers and network security professionals. Packet sniffers are of great value to any developer creating sophisticated Internet applications. A packet sniffer is a fine instrument with which we can examine in a living software system and in minute detail the processes of request and response that we learned as academic abstractions.


Chapter 16. Cookies

We have established a reliable data link between client and server: A data structure is composed on the client and transmitted to the server. On the server, PHP parses the data structure and responds to its contents by sending back an XML object to the receptive Flash client.

What is missing is depth: Flash creates the XML string from hardcoded fragments in an inflexible manner. It uses the XML response not really as data but as a trophy of successful communication, displayed as a string, not interpreted as data.

In this chapter we fill out the picture by demonstrating a little intelligent use of the XML by Flash, as well as honest generation of an XML structure. Along the way we learn to set and read cookies.


ActionScript XML Objects

We keep all the code inside the login MovieClip. But first we reorganize the timeline into two parts: a single frame called Init and another called Loop. The Init frame is executed in the beginning and only once. (It can be considered the constructor of this MovieClip object, to use the language of object-oriented design.) The loop executes continually. Its only real job is to update the stars variable so that it always has the same number of stars as the characters in the password (Figure 16.1).

Figure 16.1. Echoing with Stars

First we can upgrade the showme() function. This is the onLoad() handler, which is called by Flash when the XML data is completely loaded. Such handlers are passed a parameter that indicates a successful load or a failure.

Our more mature showme() gracefully deals with failure. In this version, a load failure causes showme() to generate its own XML content in the response object that simulates the server's error report. All code that has been written to handle server error reports can now automatically handle this communication failure, as well.

ActionScript
function showme( ok )
{
  if( !ok )
     this.parseXML("<RESPONSE><DENY error=\ "404\">
                     Server not found.</DENY></RESPONSE>")
  this.findElements();
}

Even better, showme can now handle success well. It starts the findElements() method that walks through the document tree searching for elements it recognizes. When it finds either an <ACCEPT> or <DENY> element, it executes the matching method.

Notice that the xmlAccept() and xmlDeny() functions are bound to the login MovieClip—they exist in the login namespace. On the other hand, the findElements() method is a member of the xmlResponse object. An XML object's methods execute in the _root namespace—so how can they call methods in another, relative namespace?

One simple way to make the methods addressable would be to give the instance of the login object a known name and absolute position in the swf structure. The methods would be called easily (for example, _root.loginDialogue.xmlAccept()), but this compels us to commit to an inflexible structure.

It would be much better to create a functional MovieClip object that is self-contained. Doing so requires us to maintain in the xmlResponse object a property called target, which points back to the login MovieClip that created it. this.target resolves to the namespace of the login instance so that this.target.xmlAccept() addresses the acceptance handler in that instance of login. (See the discussion of Execution Context in Chapter 8.)

ActionScript
function findElements()
{
    if( this.nodeName eq "ACCEPT"  ) this.target.xmlAccept(    this)
    if( this.nodeName eq "DENY"    ) this.target.xmlDeny(      this)
    for( var i=0; i<this.childNodes.length; i++)
       if( this.childNodes[i].nodeType ==1 ){
          this.childNodes[i].target      = this.target;
          this.childNodes[i].findElements= this.findElements;
          this.childNodes[i].findElements();
          }
}

Right now, these two functions do little. They display simple messages. In a real implementation, the xmlAccept() function will admit access to the site. The xmlDeny() function would be more or less unchanged: it just displays the error message and allows the user to try again.

Note that in this example, the text display for acceptance is hardcoded and generated on the client side. The text display for denial is extracted from the XML object and is typically generated on the server side (although a moment ago, we saw that showme() can also inject its own client-side denial message into the object). When things work properly, we don't care about the details. But when something fails we need to know what went wrong—and there are lots of possibilities.

ActionScript
function xmlAccept( element  ){     statusline= "hello—i have been
  accepted"; }
function xmlDeny( element )   {     statusline= getText( element); }

A <DENY> element node launches the getText() function. That function formulates the error message by concatenating all the text nodes that are immediate descendants of the <DENY> element node.

ActionScript
function gettext( element )
{
  text="";
  for(n in element.childNodes)
       if( element.childNodes[n].nodeType==3 )
           text+= element.childNodes[n].nodeValue;
 return text;
}

NOTE

While this form of getText() is certainly adequate as a first approximation and for demonstration, it is not reliable enough for release code. Release code must anticipate improbable conditions and potential evolution.

Today's server process might generate simple error text, but future servers might supply a lightly marked-up string. (Many Apache error messages, for example, include bold and italics.)

Imagine if this simple getText() function with its one-level-deep search got the following message, whose meaning and urgency are pretty clear:

<DENY>You must <BOLD>NEVER</BOLD> try this again!</DENY>

The user would see a slightly different message:

You must try this again!

This is because the error message appears as an object tree with these nodes:

<DENY> (element)
   |  You must (text)
   |  <BOLD> (element)
   |     |     NEVER (text)
   |  try this again! (text)

When getText() analyzed the <DENY> object, it only concatenated the text nodes immediately below the <DENY> object. The word NEVER is one level further down, within a <BOLD> node.

The submit function (called by a button press or the Enter key) has few modifications. Most notable are the two lines where the function findElements() is assigned to the xmlResponse object as a method by the same name. The target variable, discussed earlier, is established first in this code. It is set to act as a link to the login MovieClip instance.

The submit() function sets up two XML objects—xmlRequest and xmlResponse (Figure 16.2). But remember that submit itself runs in the namespace of the login instance. The variable this resolves to the address of the login instance (perhaps _root.mylogin ). It is this link that is saved in the target property of the xmlRequest, along with the findElements routine that require that address.

Figure 16.2. Interaction between login and Its XML Objects

Even though findElements() is coded on the same frame as submit(), it is installed as a method of xmlResponse. When it runs, this does not resolve to the login instance as it does for submit (and for xmlAccept()). It resolves to the address of xmlResponse. That is why it requires the target variable. Within the XML object this.target has exactly the same meaning as this does in the other functions on this frame.

ActionScript
function submit()
{
 login= "<LOGIN>"
           + "<USERNAME>" + username + "</USERNAME>"
           + "<PASSWORD >" + password + "</PASSWORD>"
       +"</LOGIN >";

  xmlRequest = new XML(login);
  xmlRequest.contentType="text/xml";
  xmlResponse = new XML("");
  xmlResponse.onload = showme;
  xmlResponse.findElements = findElements;
  xmlResponse.target = this;
  xmlRequest.sendAndLoad( "authorize.php", xmlResponse );
  statusline="WAITING for XML";
}

When this code is compiled and loaded to the server to run with its PHP script, the results are satisfying (Figure 16.3). Despite its anticlimactic resemblance to earlier screens, the screen in the figure represents considerably more control—the XML is causing functions to be fired off in the ActionScript code—and now there are few limits on what we can do with XML.

Figure 16.3. Flash Driven by XML from Server
    I l@ve RuBoard


Stateless Persistence

Cookies are conceptually simple. They are tiny text files written on the client's computer. Though they are written on the client's file system, it is the server (classically) that reads and writes the cookies.

The advantages to the software designer are great. The history of interacting with this client can be easily stored and recovered. For example (and this is the example we will use), the most recent login parameters can be saved and restored. But this is just a start. There are plenty of complex and subtle ways to use the power of persistence to create personalized web experiences.

In general cookies provide a way to recognize the user throughout a session and across sessions. Remember that HTTP is a stateless protocol. Without some explicit mechanism, there is no identification of the multiple independent HTTP dialogs that make up a single web experience. A web developer must work hard to connect the click that tosses a book into a shopping cart with the click five minutes later that brings the shopper to the checkout stand. And the developer must work harder still to recognize the shopper when she returns in a month to shop some more. This problem occurs not just with shopping but with collaborative work, game play, chat—almost every web-based activity.

Cookies help immensely with this problem. But they have a few problems of their own:

  • They are limited in size. Although they can be as large as 4K, they are transmitted with every HTTP transaction in the target domain, so large files will create huge, useless overhead.

  • They are tied to the machine, not the user. This creates problems for those who share a computer (a family, say) or those who access the Internet on a variety of machines.

  • They excite paranoia. Many proponents of web privacy worry about the potential for cookie abuse.

The first two problems can be solved by an efficient data strategy and a sensible approach to persistence.

A smart designer retains the detailed history of each individual in a database on his own server. Cookies should provide only a tiny key into that database, not the data content itself. Cookies can be set to expire at the end of a session, or to last up to five years.

Persistent cookies last longer than a single session. They make it easy for the developer to supply the database key to the returning user. If the user is on another computer, the key can be entered manually. Usually this spawns a nonpersistent cookie, which maintains the visitor's identity through the session.

The widespread fear of cookies—is trickier. Many people are unconcerned, and many are adamantly protective of the integrity and isolation of their hard drive. Most fall in the middle, abandoning vague objections in the face of ubiquity and in the name of convenience.

On the one hand, giving an outside entity permission to write to a local file system compromises principles of security. It is possible for an evil marketeer to assemble a composite of your activity on his site. (After all, that is a cookie's basic purpose, except the evil marketeer part.) But there are strong safeguards.

It is not possible for a cookie to be used to monitor general Internet activity. Cookies can be read only by the servers that create them. The server specifies the domain and the directories for which the cookie is valid, but the domain must be the server's own.

It is not possible for the writing of the cookie to perpetrate destructive mischief on the file system. While it does indeed perform a file write, that operation is hidden from the software and managed entirely by the browser.

Cookies are just text strings. They are not malicious agents that can execute instructions of any sort.

It is not possible for cookies to somehow read the identity of the user, the user's machine, or the user's e-mail address. The only information they report is information the user created while visiting the site.

As a web user, you may have mixed or even negative feelings about cookies. But as a web developer, you wish they were accepted universally. No other technique matches cookies for convenient, respectful, and reasonably reliable identification of users. Every HTTP request to your server identifies its sender transparently and absolutely effortlessly.

In fact, the worst downside is that cookies can be turned off. You will need to decide whether you will support both cookie-enabled and cookie-disabled browsers. The alternatives are ugly: Usually the identity is either embedded as GET variables in each URL or is buried again and again in obscure hidden fields in POSTed HTML forms. The GET technique is highly exposed and requires code attention at every link to concatenate the variable ID with the fixed URL. The POST technique has even more code overload, where every link must be formulated as the POST of an HTML form. It also exposes the authentication system to the curious visitor, but less obviously than in GET.

Some developers create duplicate mechanisms to support both cookies and some alternative identity scheme. Others bravely decide that their site (or features of it) will simply require cookies (just as they decide a site or a feature set requires Flash). This is generally a business decision, not a technical one. A developer can present the trade-offs, while the publisher makes the call. Since the trade-off is usually ease of development versus size of potential market, expect seat-of-the-pants decisions to usually go against you.

Even older surveys (2000) report disabled cookies in about 10 percent to 14 percent of browsers and trending downward. (The number varies widely depending on demographics.) The value of this market segment can best be balanced against the misery of developing both a cookie and non-cookie version when misery is expressed in terms of budget and schedule. Good luck.

Coding Cookies

Although JavaScript programmers can directly manipulate cookies from within the client code, we cannot in ActionScript. We treat cookies in the classic manner, as data belonging to the server but residing on the client machine.

All code that explicitly reads or sets cookies is server-side PHP code. The ActionScript code reacts to the events the cookies set in motion. In our example, we want the system to do the following:

  1. Allow the user to fill in the username and password.

  2. Record name and password in a cookie.

  3. Append the cookie to all transactions in the session.

  4. Transparently retrieve the information on next login.

We already have step 1—though we must be careful not to break it.

Step 2 is straightforward PHP code; we set a cookie as soon as we have a new valid username and password. It requires no ActionScript.

Step 3 is automatic.

Step 4 requires cooperation between ActionScript and the server. The server is quite capable of reading the cookie and transmitting it to Flash, but Flash must initiate the transaction. So as this MovieScript initiates itself, it will send a request to the server and examine the response. This transaction will be an XML exchange.

In fact, we implement this feature by simply augmenting the XML dialogue we already have developed. In it, the client sends <PASSWORD> and <USERNAME> elements upstream and receives an <ACCEPT> or <DENY> token coming downstream. We extend this a bit so that the ActionScript client can intelligently handle <PASSWORD> and <USERNAME> elements when they arrive coming downstream from the server.

XML Cookies

The format of cookies follows url encoding. The set of cookies is a flat array of string variables arranged in key/value pairs. Each cookie is a single element in this array. The header of our transactions includes something like this:

HTTP
Cookie: USERNAME=anonymous; PASSWORD=bluefish;

This information is adequate for our purposes, but it lacks the depth of XML, such as hierarchy, attributes, and indirection. So we aim instead at this:

HTTP
Cookie: login=<LOGIN><USERNAME>anonymous</USERNAME><PASSWORD>bluefish
  </PASSWORD></LOGIN>

This code affords plenty of flexibility. If you examine a raw header (with a packet sniffer, for instance), you will see that this string is url encoded, so it actually looks like this:

HTTP
Cookie: login=%3CLOGIN%3E%3CUSERNAME%3Eanonymous%3C%2FUSERNAME%3E. . .

But this format is transparent when using PHP to set and read the cookie.

While XML is a bit less efficient in bandwidth, this shortcoming is not significant in any likely scenario. It is more efficient in preserving a different resource. The cookie spec requires that browsers be able to store 20 cookies per domain. It is certainly conceivable that a publisher might want to retain no information about visitors and instead save all history to cookies on the browser's machine. Reasons to do this might involve respecting the Children's Online Privacy Act or a fear of security compromise from hackers on the one hand or a subpoena on the other. Or the developer might simply want to avoid creating and maintaining a real database when all that's needed is casual identification.

In such a scenario, the limit of 20 cookies could be far too little. XML by definition collapses all its variables into one element and thus uses only a single cookie, regardless of its contents.

Client Side

Only a little new ActionScript is required. So far only the user has been able to input the name and password. We need to let that data be set by the XML input. This requires just two tiny access functions (to write the username and password) and two simple cases to find those elements and call the appropriate function.

ActionScript
function xmlAccept( element  ){     statusline= "hello—i have been
  accepted"; }
function xmlDeny( element )   {     statusline= getText( element); }
function xmlUsername( element){     username  = getText( element); }
function xmlPassword( element){     password  = getText( element); }
function findElements()
{
    if( this.nodeName eq "ACCEPT"  ) this.target.xmlAccept(    this)
    if( this.nodeName eq "DENY"    ) this.target.xmlDeny(      this)
    if( this.nodeName eq "PASSWORD") this.target.xmlPassword(  this)
    if( this.nodeName eq "USERNAME") this.target.xmlUsername(  this)

Now all we need to do is call the server twice. We call the server first to see if it will supply a username and password. If it does, it is because our code just supplied them in the header of our request. This means that they have already been recorded on this machine as cookies. We call the server again when the user punches the button or hits Enter. Both those calls can use the submit() function, which packages the current username and password into an XML object (xmlRequest) and sends it to the server. It takes the returning XML (xmlResponse) and processes it with findElements().

This XML traffic is carried in the content bodies of the HTTP request and response. A parallel and independent cookie traffic is occurring in the HTTP headers. It is not as symmetrical as the content traffic, where the server and client exchange identical data packages.

The server can, on any response, insert a set-cookie() command into the header. The client, with every request (in the domain for which it is valid), declares the cookie and its value to the server.

By adding an immediate call to submit() when login initializes, we ask the server if we have a meaningful cookie. (We can't look at our own headers directly. Like a man with something written on his forehead, we need some help.) If the server finds a name and password in a cookie, it will pass it back to us. It will be found by findElement(). When it is assigned to the appropriate variable, it will automatically fill in the form (Figure 16.4).

Figure 16.4. An Immediate Call to submit()

Server Side

The muscle is in the PHP. Let's begin by identifying the cookie. The cookie variable we are interested in is called login. As with all cookies, login appears to the PHP script as a global variable, accessed either as a member of the $GLOBALS associative array (as $GLOBALS["login"] ) or by its own name $login after that name has been declared global. We will use the latter method.

global $login;

This script is called in four situations:

  • New user, first contact

  • New user registers

  • Old user, first contact

  • Old user signs in

To distinguish these four conditions, the server code must look to see if there is a name/ password cookie in the request header and whether there is a name/password XML object in the request content. This yields the four states in Table 16.1.

Table 16.1. Login State Logic
No XML XML
No cookie

New user, first contact

It has neither cookie nor XML because it is running for the first time on this computer. The server returns empty XML and lets the user fill in a blank form.

New user registers

The user has submitted a name and password for the first time. The server(after presumably communicating with its database) sends XML to authorize or deny access and a set-cookie() command to record this user's ID.
Cookie

Old user, first contact

As soon as the client boots up, it sends a cookie containing the ID from the last session and an XML containing nothing because the user has not seen the form yet. The server directly returns the cookie's information (already in XML format) to the client, where it is presented to the user for approval.

Old user signs in

The user has either confirmed the ID suggested by the cookie or typed in a new one over it. We do not need to differentiate between these cases or even from the previous case. When- ever XML arrives at the server, we use it for authorization and save it as a cookie.

Our PHP code must determine which of these four states is appropriate.

PHP
if( strlen( $HTTP_RAW_POST_DATA ) < 48){
        header( "Content-type: text/xml");
        if( strlen($login) < 48) echo "<empty/>";
        else                    echo $login;
        }

Note that our test for meaningful content data is to measure the size of the XML string. This works fine, since we know that the username/password object will require more than 50 characters if it is not empty. But it is a dangerous way to code. It ignores the extensibility of XML, a feature important enough to name the protocol after. In the future, someone may wish to add another element to this XML package (maybe <TEAM>), and that might cause even the empty structures to exceed 48 characters and be misread as full structures. Better coding technique is strongly recommended—and relatively easy to imagine—but omitted here for the sake of brevity and clarity.

In this fragment, the server detects that no meaningful XML has arrived. It returns an XML package based on the cookie status. If the login cookie is too small to be meaningful, a nearly-void object is returned. But if login looks good, it is sent back to the client. Remember that login is an XML cookie. The cookie itself ("login={ string} ") is a flat URL-encoded pair. But the { string} is pure XML. Labeling it as text/xml and echoing it back to the client works perfectly.

The next code handles the case where the user is submitting an ID for authorization (i.e., there is a meaningful XML upload). It is processed the same as the previous code, except for two things:

else   {
   $xml_parser = xml_parser_create();
   xml_set_element_handler(       $xml_parser, "startElement",
                                  "endElement");
   xml_set_character_data_handler($xml_parser, "characterData");
   xml_set_default_handler(       $xml_parser, "unknownXML");
   if (!xml_parse($xml_parser, $HTTP_RAW_POST_DATA, true ))
       die("XML error: xml_error_string(xml_get_error_code($xml_parser))
           at line xml_get_current_line_number($xml_parser))" );
   xml_parser_free($xml_parser);
  setcookie ( "login", $HTTP_RAW_POST_DATA,
               mktime (0,0,0,date("m"),date("d"),date("Y")+1) );

Setting the cookie must precede all the other header commands. This is an HTTP constraint. Recall another constraint: PHP requires the header commands to precede any other output, even whitespace.

The set-cookie() command takes six parameters:

  • Name The cookie's name cannot have whitespace, a semicolon or a comma.

  • Value This string associated with the cookie can be omitted, but that tells the client to delete any existing cookie with this name and namespace.

  • Expires This is a date in Unix format. In the previous code, mkdate() is used to generate a date in the correct format that refers to local midnight one year from today. Omission of the date tells the client that this is an ephemeral cookie that should be used only during this session.

  • Pathname This directory path determines the scope of the cookie. The browser combines pathname and domain to create a mask against which URLs are matched to select cookies. The pathname "/" admits the entire domain.

  • Domain This is the domain in which the cookie is valid. The server that sends the cookie must be within this domain, so generally this parameter is omitted and defaults to the server's domain.

  • Secure The final parameter is the optional secure flag that prevents the cookie from being transmitted in cleartext—only on secure socket layers.

PHP
header( "Content-type: text/xml");
   if( $theElements[ "USERNAME" ] == "anonymous")
      if( $theElements[ "PASSWORD" ] == $anonymousPassword )
          echo('<ACCEPT privilege ="guest"/>');
      else
          echo('<DENY error="404">Incorrect anonymous password</DENY>');
   else {
       echo "<RESPONSE>";
       reset( $theElements );
       while (list($elementName, $elementContent) = each( $theElements ) )
          echo "<$elementName>$elementContent</$elementName>";
       echo "</RESPONSE>";
       }
   }

Finally we have the case where an identity cannot be authorized. The server quickly generates a new XML object from the variables it was sent and passes it back to the Flash client. This is hardly the behavior a real-world server would exhibit, but it is useful for us early in our experimental development.


Conclusion

We started this chapter with a solid treatment of XML within Flash. We wrote and read XML documents using very natural ActionScript objects. We populated these objects with our own event handlers and extended them with our own methods. We even developed the notion of pointers to our own MovieClips.

We created response and request XML objects. We even brought these ideas together when our response and request objects managed the same XML elements. We now had a data object that we could share with the server process. We did just this—passing it back and forth between the two applications multiple times at different points in the authentication process.

We carefully considered cookies. We developed a case for XML cookies and implemented them. As we studied cookies and Flash XML objects, we developed a functional login system.

We hardcoded a special account name and password and walked through the process of accepting the user, setting the cookie and then later remembering the cookie and automatically readmitting the user.

But what happened when we finally got to a real user login? It was anticlimactic. We did nothing interesting at all—just echoed the request data.

This is because we cannot do much with a login request until we can access a database. In previous chapters, we studied the fundamentals of databases. Now it is time to get to work. We are finally ready to put everything together in a complete functional system.


Chapter 17. Three Tiers

In this chapter we drive the golden spike that connects the user-interface code of the last few chapters with the database technology that preceded it. In this chapter we finally create a true three-tiered application with solid client, application, and back-end software.

In the past few chapters we have built a password checking system. However, it would be an absurd architecture to make a system that required changing the codebase every time a new user was added (though perhaps it would be one way of assuring job security). Instead, we use a MySQL database to store the account information and use PHP middleware to retrieve the data.

Working in such a model is an excellent example of a three-tiered solution (Figure 17.1). We have the first tier, the client-side Flash, which then works its way up the line (via XML) to our middle tier, PHP. The PHP binds the first tier to the third, our MySQL database.

Figure 17.1. Three-Tier Design
    I l@ve RuBoard


Database

The database is the last portion of the solution we must add. In previous chapters, we created much of the code we need. The database accessing PHP code must draw on that work.

Designing the Database

We start by designing a table in which to store the users' account information. Obviously we will need to store the usernames and passwords so that the users can log in. Probably we want to store their e-mail addresses too, either for sending out updates, building a spam list, or informing users of pertinent news, such as the cancellation of an account due to inactivity.

Depending on what kind of system we are building, we may wish to gather marketing data, such as real names, postal service addresses, and such. Let us assume that we are not marketers and the marketers have yet to design our database. So we want to create a system that will work easily with a database that has not yet been designed.

Although more difficult, we can write PHP code that at runtime interprets the structure of the table.

For now, let it suffice to call the table Members. The only two columns we know exist are called UserName and Password, which hold, not surprisingly, the users' names and passwords, respectively.

To create this code, it is necessary that we introduce a few functions we don't already know. Most of them are new variations on old functions that hark back to previous chapters.

Compound Queries

We know that to log in, users will need proper user names and passwords. If the pair submitted is not found in our database, an error should be returned. To see if the names and passwords match, we use a logical operation.

PHP
$Test = MySQL_query("select * from Members
            where UserName = $User && Password = $Password");
$Is_Valid_UserName_and_Password = MySQL_num_rows($Test);
                           // 0 for false, anything else for true...

Like a programming language, the query syntax of MySQL allows us to build complex queries with any combination of the AND operator (&&), the OR operator (||), and the NOT operator (!).

Fetch Revisited

We have learned that a query to the database returns a result table, and we have learned how to fetch a sequence of data from that result. We did not discover earlier that we can also fetch information about the columns themselves. In this way we can move through a result column by column just as we formerly moved through row by row.

We can use automatic indexing, which takes advantage of the internal pointer in every PHP array. This pointer starts at the zeroth element and can advance itself during a fetch. We direct one query command to the database and choose from a myriad of related commands to examine the result table.

The Result that we pass to the fetch_field(), field_seek(), and num_fields() commands (learned later in this chapter) can be one of two kinds. We can use the result table that is returned by the common data record query, like the ones that we learned to do in Chapter 13:

PHP
$res = MySQL_query ("select * from Answers where WhichQuestion = $sqlQUIN");

Alternatively, a special command retrieves information that describes the table structure itself rather than the content of the tables.

PHP
MySQL_list_fields (Database, TableName, OptionalLinkIdentifier);

This command requires specifying a database, which can allow us to search through databases for a specific table set up in a particular way.

NOTE

If we want to look through databases for a specific table, we need a list of all the databases. We can get it by using a MySQL_list_dbs() command. Similarly, we can get a list of tables by using MySQL_list_tables(Database). Both return a result from which MySQL_tablename(Result, Offset) can read individual names. The MySQL_num_rows() command also works to count the number of tables found.

Another command simply moves the internal pointer without returning any data:

PHP
MySQL_field_seek(Result, New_Pointer_offset);

It is important that we do not run over the edge of the result in seeking fields. To this end, we can use a simple command to return the number of fields:

PHP
MySQL_num_fields(Result);

We can fetch fields, that is, information about columns, from the result table, using the proper command. This returns the field as an object.

PHP
MySQL_fetch_field( Result, Optional_Index);

Starting at 0, the fetch_field() command gets each field in order, silently advancing the internal pointer. This is like the fetch_row() commands. Adding the Optional_Index forces the internal pointer to a new position and then fetches a new field. The pointer advances normally during this fetch, resting at Optional_Index +1.

The fetch_field command returns an object. Table 17.1 lists its properties.

Table 17.1. Properties of the Object Returned by fetch-field()
PROPERTY NAME | VALUE STORED | VALUE TYPE | NOTES
name | Name of field | String
table | Name of table | String
type | Name of type | String | Some types, e.g., TinyInt, are reported as Unknown.
blob | Is field a blob | Boolean
numeric | Numbers only | Boolean | Times, Dates, Ints, and such are all numeric.
unsigned | Int, range 0–X | Boolean | Whether an Int is unsigned or not
zerofill | Is the column? | Boolean
max_length | # of characters | Integer | All types have this property, digits for numerics.
not_null | Can field be null | Boolean | Is this a required field? Or can it be empty?
primary_key | Self-Evident | Boolean
unique_key | Self-Evident | Boolean
muliple_key | Self-Evident | Boolean | Key that is not unique

Also, five commands can take this information and return values. They follow the familiar format

PHP
MySQL_field_FUNCNAME(Result, Offset);

The five functions that can replace FUNCNAME are listed below:

  • Flags Returns all boolean properties with the exception of numeric, as well as enum and auto_increment as string with each applicable flag written out. Sample output may read "not_null auto_increment unsigned". The function explode() may be helpful in parsing this string into an array of individual flags.

  • Name Returns a string with the name of the field (that of the column the field is in) as a string.

  • Len Returns as an integer the maximum length in characters/digits that a field can hold.

  • Type Returns as a string the type of field: int, BLOB, timestamp, and so on. Some variations (e.g., TinyInt) are returned as unknown types by MySQL.

  • Table Returns a string with the name of the table this field comes from. An offset is required, because it is possible to have many tables contribute columns.


Flexible Middle Tier

Now we are prepared to build a PHP script that, simply by being pointed to the right database and table and being given some very vague specifications (the existence of two columns and their names), can build an interactive form for displaying and editing all the records. We write a script that allows us to add a new user and to log returning users in properly and allow them to review and update all the data about their record. To do this we pass an XML object, called <member>, back and forth. Each tag on a child of <member> follows the same pattern.

Output
<name-of-data type valid>Current Value</name-of-data>

The name-of-data will be the same as the name of the column that that datum will be placed in.

The type attribute will be either "Fixed", "Number", "String16," or "String32". Number types ask the user to input pure numbers. "String16" indicates a string with a length limit of 16 characters. The actual type of a fixed element is irrelevant because the client does not permit it to be edited.

Obviously, to simplify this portion of the code, we introduced to the other tiers knowledge about the limited variety of types actually in this database. If we were to extend our code to allow any type to be used, we would learn nothing special and would only increase the likelihood of contracting carpal tunnel syndrome.

Stateless Design

Each transaction between client and server is independent; there is no automatic way to follow the history of a session (there really is no session). It is best to infer the state of a transaction from the client's XML upload (Figure 17.2). We have six distinct actions that can be triggered by the incoming data:

Figure 17.2. Flowchart for the Stateless Database-Interaction Program. Yes, we must write code that does all this.

  • New login If a blank XML object is received by the server, it should return the empty fields UserName and Password.

  • New user If the client sends a UserName that is not in the database, the server sends all blank fields (except UserName and Password) and lets them fill in their information.

  • Invalid login If the UserName is in the database but the Password is wrong, the server sets the invalid flag within the <Password> tag and returns the UserName and Password.

  • Valid login If the client sends a matching UserName and Password—and nothing more—the server returns the complete record for that user.

  • New record If the client sends data for a nonexistent UserName, the server creates a new user record in the database.

  • Record update If the client sends more data than just a correct UserName and Password, the server changes the record for that UserName to match the data just received.

Note that we change all the data in the record, including the Password, but we never alter the UserName. One reason is that this makes the code much easier to write, but it also allows us to have the password change. The reason for this is that, client-side, to submit more than UserName and Password we have to have already either logged in or started the process of generating a new record. From that point on the UserName is fixed. Since we know that the password was known earlier by this client, we can accept any other password they give us as valid.

PHP
<?
header( "Content-type: text/xml");
echo "<Member>";

// Parser taken from Chapter 15
global $HTTP_RAW_POST_DATA;
  $theElements=array();
...
reset( $theElements );
//End of borrowed code (Puts XML in Array titled $theElements)

Is the array empty? We need at least a username and password!

PHP
if(sizeof($theElements)==0 )  {
  echo(  "<UserName type = \ "String16\ " required = \ "True\ "/>" );
  echo(  "<Password type = \ "String16\ " required = \ "True\ "/>" );
  die("</Member>");
  }
$UserName = $theElements["UserName"];
$Password = $theElements["Password"];
MySQL_connect("sql.useractive.com","bigfun","[REDACTED]");
MySQL_select_db("bigfun");
$is_old = MySQL_query ("select * from Members where UserName =
                        \ "$UserName\ "");

If it is a new record, add it if all the required information is there.

PHP
if( !MySQL_num_rows( $is_old ) )
  {
  $TableArchtype = MySQL_list_fields("bigfun", "Members");
  for($i=0;   $i < MySQL_num_fields( $TableArchtype );  $i++)   {
    $Field = MySQL_fetch_field ($TableArchtype, $i);
    if($Field->not_null and !$theElements[$Field->name])
      $Incomplete = 1;
    if($theElements[$Field->name])  {
      if($NotFirstTime++)  {
       $ColumnsUsed .= ", ";
       $ValuesAssociated .= ", ";
       }
     $ColumnsUsed .= $Field->name;
     if($Field->numeric)
        $ValuesAssociated .= $theElements[$Field->name];
      else
        $ValuesAssociated .= "\ "".$theElements[$Field->name]."\ "";
     }
  }
 if(!$Incomplete)
    MySQL_query(
       "insert into Members ( $ColumnsUsed ) values ( $ValuesAssociated )");
   }

Now see if the password is valid.

PHP
   $is_valid = MySQL_query ("select * from Members where UserName =
                             \ "$UserName\ " && Password = \ "$Password\ "");
  if(!(MySQL_num_rows($is_valid) or $Incomplete and (sizeof($theElements) >
     2))){
         echo '<UserName type = "Fixed">'.$UserName.'</UserName>';
         echo '<Password type = "String16" valid = "False" required="True">
          Try again </Password>';
         die("</Member>");
         }
      MySQL_free_result($is_valid);
      MySQL_free_result($is_old);
  $result = MySQL_query("select * from Members where UserName =
                         \ "$UserName\ "");
  $MemberData = MySQL_fetch_row($result);
 $TableArchtype = MySQL_list_fields("bigfun", "Members");

Output what data we have and update records.

PHP
  for($i=0;   $i < MySQL_num_fields($TableArchtype);  $i++)   {
   $Key = MySQL_field_name ($TableArchtype, $i);
   $Field = MySQL_fetch_field ($TableArchtype, $i);
   if($Key == "UserName")          $Type = "Fixed";
   else
        if($Field->numeric)        $Type = "Number";
        else
         if(MySQL_field_len($TableArchtype, $i) == 16)
                                   $Type = "String16";
         else                      $Type = "String32";
      $Value = $theElements[$Key]? $theElements[$Key] : ( $MemberData[$i]?
        $MemberData[$i] : "Fill Me In");
      if( !$Incomplete )      $success = MySQL_query("update Members set
        $Key = \ "$Value\ " where UserName = \ "$UserName\ "");
      echo "<$Key type=\ "$Type\">$Value</$Key>";
     }
  echo "</Member>";
?>

Code Deconstruction

PHP
if(sizeof($theElements)==0 )
  {
  echo(  "<UserName type = \ "String16\ " required = \ "True\ "/>" );
  echo(  "<Password type = \ "String16\ " required = \ "True\ "/>" );
  die("</Member>");
  }

This is a hardcoded response to an empty <member> XML object. Since the $theElements array consists of every tag within the <member> element, if the array is empty there must be no tags, hence no username and password.

This is an initial request. The user requesting access needs to supply a username and password. So we send out the proper empty tags, letting the user fill them in. Then when the XML returns, it can be easily processed.

PHP
$is_old = MySQL_query ("select * from Members where UserName =
                        \ "$UserName\ "");
if( !MySQL_num_rows( $is_old ) )

Essentially, these two lines test to see if a record for that username currently exists. If the record does not exist (the if is true), we know that this is a new user.

PHP
{
$TableArchtype = MySQL_list_fields("bigfun", "Members");
for($i=0;   $i < MySQL_num_fields( $TableArchtype );  $i++) {
   $Field = MySQL_fetch_field ($TableArchtype, $i);

This code generates a result consisting of field names, not data records. We then loop through every field, assigning each to the variable $Field.

PHP
if($Field->not_null and !$theElements[$Field->name])
   $Incomplete = 1;

This code tests to see whether a field is required, and if so whether it was omitted by the user. If a required field is missing, then the record is still considered new. No data is added to the data base (which would reject any attempt to add the data because of the missing required value). Further, if this is a new record, the code for testing the password is skipped.

Other than these required values, the database can accept any sparsely filled-in record. It looks to see if there is a value for each field.

PHP
if($theElements[$Field->name])
{

If the field's name matches a tag, we want to insert that value into the database. To do so, we build up concatenated strings. One consists of the names of the columns we have data for. The second stores the associated values in the same order.

PHP
if($NotFirstTime++)              {
    $ColumnsUsed .= ", ";
    $ValuesAssociated .= ", ";
   }

If it is any time other than the first, we concatenate a comma and space to the end of our strings so that the list will be readable to MySQL.

PHP
 $ColumnsUsed .= $Field->name;
 if($Field->numeric)
     $ValuesAssociated .= "\ ""$theElements[$Field->name]."\ "";
 else
     $ValuesAssociated .= "".$theElements[$Field->name]."";
 }
}

Then concatenation of the name and the values occurs. If the field is not numeric, quotation marks need to be placed around the value to make it a string literal when MySQL reads it. The backslash double-quote is the escape sequence that produces a double-quote within a double-quote wrapped string literal.

PHP
if(!$Incomplete)
   MySQL_query("insert into Members ($ColumnsUsed)
                values ($ValuesAssociated)");

If it is not incomplete, meaning that all the required fields were present with some value, then a new record is created. The variable name $Incomplete flags whether the record will be inserted into the database in time to be tested by the password validator. So if it is ready, the record is created; otherwise, it is not.

NOTE

If we used an AUTO_INCREMENTing field to produce a UIN, we could find its value with this query (used right after the insert operation).

MySQL_insert_id();

It returns the AUTO_INCREMENT field value in the insert operation.

PHP
$is_valid = MySQL_query ("select * from Members where UserName =
                          \ "$UserName\ " && Password = \ "$Password\ "");
if(!(MySQL_num_rows($is_valid) or $Incomplete or (sizeof($theElements) >
     2)))
   {

The if statement has three conditions. If any one is true, the script ends early and sends an invalid password tag back to the client.

MySQL_num_rows ($is_valid)

This statement is true if MySQL_num_rows ($is_valid) points to a non-empty table. There is a record whose UserName and Password fields match PHP's $UserName and $Password.

$Incomplete

This statement resolves to true if the record is not in the database and the user has not supplied enough information yet to add it.

(sizeof ($theElements) > 2)

More than two fields indicate the user has already logged in, because the client is echoing tags that the server has supplied from the database. The client's new information can be safely written to the server except the UserName, which the client software makes static.

PHP
echo '<UserName type = "Fixed">'.$UserName.'</UserName>';
echo '<Password type = "String16" valid = "False" required="True">
               Try again </Password>';
die("</Member>");
}

If the password is bad, the login fields are returned after the bad password is overwritten and the flag for invalid entry is set. Then the script exits while completing the XML packet.

PHP
$result = MySQL_query("select * from Members where UserName =
                       \ "$UserName\ "");
$MemberData = MySQL_fetch_row($result);
$TableArchtype = MySQL_list_fields("bigfun", "Members");
for($i=0;   $i < MySQL_num_fields($TableArchtype);  $i++)   {
  $Key = MySQL_field_name ($TableArchtype, $i);
  $Field = MySQL_fetch_field ($TableArchtype, $i);

Here we cycle through all the fields.

if($Key == "UserName")
  $Type = "Fixed";
else
  if($Field->numeric)
    $Type = "Number";
  else
   if(MySQL_field_len($TableArchtype, $i) == 16)
      $Type = "String16";
   else
      $Type = "String32";

This code chooses a valid value for the XML type attribute by examining the MySQL field's properties. The username is the only read-only field because it is the key for this table and therefore must be unique and static. Obviously, if a field is numeric its type is a number. Since the only things not numbers are strings, and since the only two sizes of a string are 16 and 32, we can use the length of the two to differentiate. Of course, we cheated just a tad by restrict ing the nonnumeric values, but it is easy to see how we could handle the relaxation of these restrictions.

$Value = $theElements[$Key]? $theElements[$Key] : ( $MemberData[$i]?
         $MemberData[$i] : "Fill Me In");
if( !$Incomplete ){
  $success = MySQL_query(
     "update Members set $Key = \ "$Value\ " where UserName =
     \ "$UserName\ "");
   echo "<$Key type=\ "$Type\">$Value</$Key>";
  }

First the value that will be passed back is determined. The priority of possible things passed back is new information submitted by the user, old information currently in the database, and default information (constant).

We then update the data in the table. We do this only if a record exists, so we test the new record flag in the if statement. If we want to check how many rows were affected by our update (it should be one), we report

PHP
MySQL_affected_rows();

This code returns the number of rows affected by the last query delete or update.

Finally, we format the tags and send them off.


Client Tier

The design of this client is very clean. It knows nothing at all about the forms it will be required to present. In the middle tier, our PHP program was pretty independent of the data; it asked the database what the fields were and then passed them to the client. It only knew that the UserName and Password existed and had special properties. The UserName is the primary key—the unique identifier of the record. The Password is required to open an exist ing record.

The Flash client is even more ignorant. It does not know any special data elements; it simply displays what it is told to. We create two graphical elements, a simple submit button and a generic data field. The field object can display the contents of an XML leaf element and (optionally) permit it to be edited. It can also recreate itself as XML that reflects the editing and possible validation.

The client has only to take in an XML object and dissect the data, associating each leaf element with a field display. It places the fields neatly onscreen with the submit button.

Submit Button

After the user edits the fields, he presses the submit button. The data in memory transforms itself into an XML object. This object is uploaded to the server, and a new XML object is loaded from the server. This will show an updated form, perhaps with new fields, perhaps showing which fields are acceptable and which ones might have problems.

ActionScript
function makeFields( xml )
{
    for( var i=0; i<xml.childNodes.length;  i++)
     if( xml.childNodes[i].nodeType ==1 ){
       this.attachMovie( "field", "Field"+this.n, 1500+this.n);
       clip= eval( "Field"+this.n++ );
       clip._x= this.xchild;
       clip._y= this.ychild+=24;
       clip.item=  xml.childNodes[i] ;
       makeFields( xml.childNodes[i] );
       }
}

This code attaches a field object to the current MovieClip and places it onscreen. The code also inserts the correct XML data. This code is a function of a MovieClip, not a method of any XML object. The makeFields() function is passed an element node and it runs through all the children of that node, ignoring all but the element nodes (type 1). For each element node, makeFields() instantiates a new field MovieClip. It names the new field object Fieldn (where n is the next available number) and places it 24 pixels below Field(n–1). The makeField() function copies the XML daughter element into the MovieClip as the variable item and then recurses through that node.

ActionScript
function submit()
{
   statusline= "Waiting for Server.";
   member= new XML("<Member></Member>");
   while( this.n— ) {
       this.member.firstChild.appendChild( eval("Field"+this.n).toXML() );
       eval("Field"+this.n).removeMovieClip();
       }
   this.xchild = this.ychild = this.n =0;
   member.onLoad= this.refresh;
   member.contentType= "text/xml";
   member.sendAndLoad( "member.php", member );
}

This function is hooked to the submit button. It creates an XML object called member that contains all the elements created by the field objects. As a single wrapper element, it is a legitimate XML document and perfect for passing back and forth to the server.

Each of the n fields is converted from an ActionScript object to an XML object. First, the toXML() function is invoked. It returns an XML element which is appended within the member element. The MovieClip is then removed. Then the positioning and count variable (_xchild, _ychild and n) are all zeroed out. The display is empty.

The XML object is given and the event handler and the content type must be manually set each time. Then the object is fired off to the server and exchanged for a new XML object of the same name.

ActionScript
function refresh( ok )
{
   statusline= ok? "Server response:\ n" : "Error contacting server.\ n";
   statusline+= member.toString();
   makeFields(  member.firstChild );
   delete( member );
}

After the server has updated the member object, and the status line is updated, the data is converted from XML back into ActionScript. The makeField function recursively converts XML elements into visible field MovieClips, and the XML is tossed to the garbage collector.

Field Object

A field object makes a single variable visible and editable. It displays the variable's name and its contents. The contents are editable depending on the variable's type:

  • A Fixed value cannot be edited.

  • A Number needs numeric entry.

  • A String16 is limited to 16 characters.

  • All others are limited to 32 characters.

Two flags can be set by the XML element. A false value in invalid means that the current value is unacceptable. In our design, this flag is set by the server when the SQLs data validation raises an error. Alternatively, it could be set by a local validation process within the client (or it could be set by the PHP script, as it sometimes is).

The required flag is typically set by the SQL database to warn the user that a blank entry in this field will return as invalid (Figure 17.3). There is a blank frame that allows the XML to be loaded before it is analyzed. The second frame analyzes the XML and advances to the appropriate keyframe for displaying this data type.

Figure 17.3. Field Displays a Single XML Tag

ActionScript
function setVariable( node ) {
   name= node.nodeName;
   value= "";
   for( var i=0; i< node.childNodes.length; i++)
           if(      node.childNodes[i].nodeType== 1)
                        setVariable( node.childNodes[i] );

The setVariable() function hunts through the subnodes. If it finds an element node, it recurses.

ActionScript
else if( node.childNodes[i].nodeType== 3)
   value += node.childNodes[i].nodeValue;

If it finds content, it concatenates the content into the editable value variable.

ActionScript
if(      node.attributes["type"]   eq "Fixed"   ) gotoAndStop("Fixed"   );
else if( node.attributes["type"]   eq "Number"  ) gotoAndStop("Number"  );
else if( node.attributes["type"]   eq "String16") gotoAndStop
         ("String16");
else                                               gotoAndStop
         ("String32");

Then it chooses a display frame based on its type attribute.

ActionScript
if(      node.attributes["valid"]  eq "False"   ) valid   ="INVALID";
else                                              valid   ="";
if(      node.attributes["required"]eq "True"   ) required="REQUIRED";
else                                              required="";

It decides whether INVALID and REQUIRED flags are to be displayed.

ActionScript
   if (!eval(name)) eval( name ) = value;
}

Finally, it creates a variable in its own namespace with the same name as its element and the same contents.

The field also has a toXML() function. This function is similar to an XML object's .toString() method and is used much the same way. Called as a member function of a field object, it returns an XML element.

ActionScript
function toXML()
{
   element=             (new XML()).createElement(  this.name  );
   element.appendChild( (new XML()).createTextNode( this.value ));
   return element;
}

It makes an element node whose name is contained in name and attaches to this node a text node that contains the contents of value. Of course, this is simply the reverse of the way we got name and value from an XML element earlier.

It would not be hard to insert the attributes (type, invalid, and required) into this XML with a line like

ActionScript
if ( valid eq "INVALID")      element.attributes["valid"]= "False";

But it is not necessary; the server does not use this information.


Conclusion

High fives! We have made the round trip from client query to database and back and created a true three-tiered design that isolates the presentation layer on the client from the application logic on the server from the IT fundamentals on the database server. And we did it with some style, demonstrating the beauty of a very thin Flash client and the cleanliness of an easily reentrant stateless design.


Chapter 18. Flash to the World

In this chapter we learn to link to third-party publishers of XML content. Our goal is to acquire and manipulate content in XML format from outside our own domain.

The first step is to slip past the inhibitions that Flash places on contact with others. We do so by creating a proxy service running on our own server but making contact with the external servers that Macromedia has put off limits.

Once we have created a dataflow from those servers to our Flash program, we need to understand the data. There are many popular XML formats for sharing information. We review one standard and peek at the communities that have been developing around it. In particular, we examine in depth RSS, a simple standard with several constituencies.

The techniques in this chapter are interesting and the world they open up is utterly astonishing. If you have not yet been exposed to the living network of XML information circulating through the Internet, get ready! Even in its immaturity, this flow of organized information is an enormous cascade.


Domain Perimeter

Macromedia observes a security principle sometimes known as an origin check or domain perimeter. In this case, the principle forbids ActionScript from loading XML data from third parties. More precisely, it inhibits a Flash program from downloading data from any URL that is not within the subdomain from which the Flash program itself was loaded. The subdomain is what we usually think of as an organization's domain name. In the case of a top-level domain (the TLDs are .com, .net, .org, .edu, .gov, .mil), a subdomain has two parts separated by a dot. The first part is the name and the second is the TLD itself:

  • alw.com

  • acobson.net

  • aspca.org

In the case of a country domain, a subdomain generally requires three parts—

  • bbc.co.uk

  • meretz.org.il

—but in some countries two suffice:

  • gov.ru

  • novo.dk

In all these cases subsubdomains (prepended and separated by dots, like dov.jacobson.net ) and directories (appended and separated by slashes, like jacobson.net/jesse) are allowable locations for a target file—they are still within the cardinal subdomain. But any file download from jackson.net/jesse would not be permitted. This applies not only to XML files, but to all data transfer—even submovies loaded as swf files. It regulates these functions:

  • XML.load

  • XML.sendAndLoad

  • XMLSocket.connect

  • getURL

  • MovieClip.loadVariables

  • MovieClip.loadMovie

The reasoning behind this security principle seems obscure. Naturally, it prevents us from using ActionScript as a tool for crude DoS server attacks, and it impedes somewhat the theft of net content. But such crimes can easily be performed with other readily available technologies. Limiting the reach of ActionScript greatly reduces its utility in a world that is every day more interoperative. The rule is at once draconian and futile. If a telephone manufacturer made instru ments that would dial only members of your family, this policy would have the same problems. Vandals would still manage to make prank calls somehow, and the rest of us would be terribly inconvenienced.


PHP Proxy

Our strategy to defeat Macromedia's domain perimeter policy is to create a server script that resides in our domain space. This script is not limited by the Macromedia restrictions and can contact other servers on any domain in the world. It relays requests from Flash to other servers and echoes back to Flash the foreign servers' responses. This process is called a proxy service.

Our script will accept XML of any sort from Flash. It will contact a server using either GET or POST methods. The XML it receives from the server is repeated back to Flash without any server processing.

The client (the Flash code making the request) must direct the proxy server to the foreign URL that it is trying to contact (Figure 18.1). The client sends that information in the XML package that it uploads to the proxy. This XML object has one required element, <proxy-target>, which contains the full URL of the external XML resource. It also has an optional second element called <request>. This element is used when the Flash client is uploading material to a remote server. The proxy will POST the entire contents of the <request> element without altering (or even parsing) that block of data.

Figure 18.1. The Flow of XML Across our Proxy

Code

Our server script performs four XML transactions:

  • Receive an XML package POSTed by Flash

  • Send the <request> element from that package to the URL marked as <proxy-target>

  • Receive an XML response from the server

  • Echo the XML response unchanged to Flash client

It is only the first transaction where the script must actually understand the content of the XML. In the other cases, the XML is a black-box payload that is simply delivered to its destination.

But to get from the first transaction to the second, it is necessary to isolate the contents of the <proxy-target> and <request> elements. We could set up and run PHP's event-driven parser. But it is quite a bit of work for a small task. Instead, we dissect the XML stream ourselves using string processing methods.

We begin by defining a function that yanks the content of an element out from a string of XML.

PHP
<?php
function getElementContent( $xml, $tag )
{
       $start = strpos( $xml, "<$tag" );
       $start = strpos( $xml,">", $start )+1;
       $end   = strpos( $xml, "</$tag>", $start );
       if( ($start===false) or ($end === false)) return "";
       return   substr( $xml, $start, $end-$start);
}

Three notes about this function:

  1. The parameter $xml refers to an XML code string, not an XML data object. In fact, unlike ActionScript, JavaScript, Java, and ASP, normal PHP 4.0 does not have an XML object.

  2. The end tag is found with a single strpos call. The start tag, however, is found with two calls, one to locate the first characters of the start tag, the second to find the tag's terminator (the >). This is done to skip over any attributes that may exist in this element. Currently no attributes are defined, but a later version might well place attributes on the proxy-target element (perhaps authorization information or cookies). By anticipating this possibility, we can maintain some forward compatibility and build a more robust system.

  3. The test ($start===false) uses a triple equals sign. This formulation is unique to PHP (starting with PHP 4). It requires both sides to resolve not only to the same value but also to the same type. This is extremely useful here: When strpos finds the search string it returns the position of its first character. When it cannot find the string at all, it returns a boolean zero—false.

    The problem arises when the search string is found at the beginning of the text. A value of 0 is returned by strpos, meaning that you'll find the target string starting at position 0. We cannot easily distinguish this integer 0 from the boolean 0, which means "target string not found." The identity conditional—the triple equals—does exactly that.
PHP
$msg= $GLOBALS[ "HTTP_RAW_POST_DATA" ];

First we select the text directly from the input buffer. Or we use a fake line.

PHP
if( strlen($msg) <36) $msg  ="<xml><proxy-target>http://p.moreover.com/
    cgi-local/page?c=Online%20games&o=xml</proxy-target></xml>";

The fake input is just used to test this script as it is developed. However it shows us a typ ical data package. We see that within the basic <xml> object there is only the single element <proxy-target>. This sample does not have a <request> element. Indeed if it did, there would be a serious paradox.

If present, our <request> element is an XML string that our proxy must deliver to the server with the POST method. But the <proxy-target> here is a URL to which a string of x-www-urlencoded equations is joined, setting c to "Online games" and o to "xml". Presumably these set the content and output format of the response. This URL clearly suggests the GET method, rather than POST. In fact, a POST to this server location returns

Output
Error 405: Method Not Allowed.

Now we crudely parse our XML string.

PHP
$target   = getElementContent( $msg, "proxy-target");
$dblslash = strpos( $target, "//" );
$target   = substr( $target, ($dblslash === false)? 0 : $dblslash+2);

The proxy target is isolated, and any prefix is stripped off. For instance, we want to convert "http://bigfun.net" to "bigfun.net".

The "http:" prefix to the URL instructs the browser to use the HTTP protocol. This PHP script runs on our server with no browser or any other HTTP intermediary. Instead, we learn to open up a socket directly and manually execute the steps of an HTTP transaction. The "http:" prefix does not help us, and it gets in the way. Socket software requires simple domain names: awl.com, not http://awl.com.

Note that we used triple equals again to distinguish the $dblslash result of "awl.com" (boolean 0) from that of "//awl.com" (integer 0).

PHP
$slash   = strpos( $target,  "/" );
$domain  = substr( $target, 0, $slash);
$page    = substr( $target, $slash);

Our implementation of HTTP requires one procedure for contacting the server and a separate function for requesting an individual document. So we need separate strings for the domain and the page names. We therefore split the URL at the first slash (which becomes the leading char acter of the page name).

PHP
$request= getElementContent( $msg, "proxy-request");

We snip the request XML out of the raw string. Remember, when composing this package, that <proxy-request> only is meant for packaging the request and passing it to our server script. The tags are stripped from the XML before the code is transmitted to the remote server. But the XML sent to the remote server must be well formed: it must be contained in a single element. You cannot expect the <proxy-request> element to serve that purpose. Instead, use a wrapper element that is expected by the remote service.

PHP
$method = ((strlen($request)<7)||strpos( $page, "?"))? "GET":"POST";

If no real XML request package exists or if a URL-encoded message is found, we use the GET method. Otherwise we use POST.

Now we have set up all the parameters we need to contact the remote server on behalf of the restricted Flash program.

Sockets

The Unix concept of pipes was a model in which the output of one process is routed to the input of another. It was a liberating idea, and a lot of interesting software has been written to participate in the piped streams.

Underlying the original pipes was a sense of data flowing one way—from source to sink. Pipe functionality has been stretched to accommodate more complex pathways, but it has given birth to a newer way to manage interprocess communication. The socket concept is more flexible, more platform independent, and better suited to processes running on remote machines. Sockets form the plumbing of the Internet.

Every machine on the Internet has its own IP (Internet Protocol) address. The address is of the form 209.122.43.129 and uniquely identifies a given computer. If this were the phone system, it would end there. Users would be expected to memorize (or record) a long string of digits for each person they wish to contact.

But the Internet offers a convenience. There are servers in the Internet dedicated to looking up IP addresses automatically. These are DNS—domain name servers. We type in "usa.net" and we connect to 204.68.23.29. If only Ma Bell had thought of this! In hindsight the telephone network seems so crude and so poorly adapted to human needs.

Each IP address is further divided into ports. The term sparks a nice mental picture of a physical connection, like a phone jack or a serial port, but the relationship between a port and anything physical is fairly tenuous. Better to think of it as a completely abstract mechanism for sorting incoming messages into separate channels.

Each port on the server can have a single socket associated with it. The socket is a server operating system resource. It is used to allocate the port to a software process running on the server through an action called binding. When a socket binds to software, it is committed to service that software alone. Similarly, when the socket binds with a port, a mutual and exclusive relationship is created. While the binding relationship is an absolute commitment, it is not a permanent one.

Common Internet processes tend to be found on (informally) reserved ports. For instance, a WWW server running the HTTP protocol is always at port 80. The ftp server is found at 23. One port on the server, one server socket, one software process.

However, as we all know, many clients can connect to a single server. Hundreds of browsers can contend for the attention of a single server. This asymmetry is at the heart of Internet traffic and makes server-side sockets programming a complicated art, which we address in subsequent chapters.

Socket programming on the client side is not terribly complicated. A socket can be opened as a stream with a file pointer, and it can be easily written to and read from.

PHP
$fp = fsockopen( $domain, 80, &$errno, &$errstr, 30);

This line opens a socket on the alien server at $domain. No traffic has started. There is no HTTP transaction yet and certainly no content exchange. Only the raw Internet Protocol connection has been made. HTTP is a protocol that is transported on top of an IP connection (Figure 18.2).

Figure 18.2. Protocols Support Protocols

The fsockopen() line opens the socket as stream and returns with a file pointer. Now reading the socket can be performed conveniently using the simple and familiar functions used for file i/o. The hardcoded 80 parameter is the port number, which is always 80 for an HTTP server.

The next two parameters send the addresses of variables dedicated to error reporting. Note the ampersand that precedes the variable name $errno. This is the PHP syntax that allows a parameter to be passed by reference, where a called function is passed a pointer that allows it to directly manipulate the calling function's variable. Usually parameters are passed by value, where the called function gets a private disposable copy of the variable and can never modify the original

The final parameter, simply hardcoded to 30, is the optional timeout parameter. This call will fail if a connection cannot be successfully concluded in 30 seconds.

PHP
header("Content-Type: text/xml");

The PHP command header("Content-type…") transmits a header back to the Flash client that sent the request.

Note carefully: This header is not sent across our new socket. The header() command, (like echo() and print() ) continues to use the original connection. It is not intended for the alien server. Across the new socket, the program at $domain is the server, and our proxy pro gram behaves as the client. Across the default socket the Flash program is still the client, and the proxy is the server. The goal of the proxy is to be transparent. As much as possible, we want the proxy to look like the Flash client to the remote server and look like the remote server to the Flash client.

The reason the proxy is sending the header to the Flash client is to prepare for the next line, which may require the proxy to send an error message formatted as an XML data packet.

PHP
if (!$fp)
  echo ('<ERROR errno ="'. $errno .'" >'. $errstr .'</ERROR>' );
else {
  fputs ($fp, $method." ".$page." HTTP/1.0\ r\ n");

If there is an error creating the socket, a failure message is sent to the client. Otherwise, a request is sent to the server. The request will be of the classic form:

HTTP
GET myfile.php?args HTTP/1.0

If it is a GET request, our job is done. Any parameters that needed to be sent to the server were already incorporated into the URL. They were appended to the end and separated by a question mark.

A POST request is more complex. Our proxy has to compose a fairly complicated HTTP request.

PHP
if( $method=="POST" ){
       fputs ($fp, "Content-Type: application/xml");
       fputs ($fp, "Content-Length: ".strlen( $request) );
       fputs ($fp, "\ r\ n");
       fputs ($fp, $request );
       }

If this is a POST request, our code assembles these pieces

  • The data is labeled as XML data.

  • The size of the data is indicated. This is the length of the XML string within the <proxy-request> tags.

  • A blank line marks the end of the header. (Headers are of variable length.)

  • The XML content follows.

A POST transaction is commonly used when requesting complex services in which the client submits a package of data for processing and receives a fresh XML package from the server as a response.

PHP
else   fputs ($fp, "\ r\ n");

Both GET and POST requests are terminated with a blank line. The end of an HTTP request is signaled in several ways:

  • A second blank line is inserted (the first terminated the header).

  • The Content-Length specified in the header is satisfied.

  • An internal content end marker is transmitted.

  • The socket is closed.

The first way (blank line) works fine for the methods like GET, as exemplified here. But it is unreliable for most POST data and certainly for arbitrary XML, which can include blank lines happily.

The second (content length) is the most reliable for POST data. It is what we use for the request here. Very soon, however, we will see that the content length is not always known at the time of header transmission.

The third method (internal marker) works when the process receiving the transmission is well understood and reliable. An example is the </HTML> tag that ends every web page. Most servers can rely on the browser to recognize this tag. Similarly, the end of an XML transmission is often indicated by the closing tag that finally matches the initial opening tag. This method requires a robust receiving process (for example, the proxy we are now building would fail), and it is quite ugly in the way it relies on a higher layer (HTML or XML) to supply information that is missing from a lower layer (HTTP). Returning to a telephone analogy, this would be as if the only way we could hang up a line is by saying "Goodbye" to someone on the other side who understood us. It usually works but the potential for failure should be obvious.

The fourth technique (socket closure) again uses information in one layer to correct a defect in another. But in this case, the lower layer (socket) terminates a transaction that was left open in the higher layer (HTTP). Although it is not optimal for information to cross layers, socket closure is extremely reliable. Like hanging up a phone call to terminate a conversation, it is crude but effective. It is not unlike other i/o stream reads that are terminated by the end-of-file indicator. We do, in fact, use this technique very soon in our proxy, to mark the end of the response in both the client and server roles. This technique has a major limitation: It is useful only in the response portion of the HTTP transaction. If you were to close the socket after the request portion, you would never get your response!

Now that the request is complete, our proxy waits for data to arrive. This waiting happens in the function fgets(), which does not return until a line of data appears (meaning that it finds an end-of-line or the buffer fills to the specified length—e.g., 128). The feof() function signals when there is no more data to be had.

while (!feof($fp) &&  strlen(trim(fgets ($fp,128))))
            ;

This loop gets each line, trims away the whitespace, and checks the length of the string. If the string is not empty, the loop simply tries again. It is seeking the first blank line. By doing so, the code skips past the header.

Ignoring the header is extremely shabby and limits our proxy to a simple demonstration program that allows us to peek across the domain perimeter in the cases where everything goes smoothly. Our stated goal of transparency is difficult to claim when we toss out the header rather than pass its information on to the client.

Even worse, our code does not read the headers. As we explore the very unstable world of public XML servers, we are bound to run into many errors and incompatibilities that we would be wise to observe. Sometimes we will be able only to fail gracefully. Some errors we will actually be able to correct (such as redirection). Remember that in its role as client, the PHP script gets no help. There is no browser involved; we are directly connected to a naked socket, and we are fully responsible for managing the client side of this session.

That said, let's indulge our impatience and raise a quick and cheap periscope to peer over the domain walls. If we like what we see, we can return to build out a robust proxy that reads and respects the headers.

    while (!feof($fp))      echo fgets ($fp,128);
    fclose ($fp);
    }
?>

Simply, we echo to our (Flash) client every line we receive from the remote server. When the remote server finally closes the connection, generating the end-of-file signal in feof(), we do the same thing to our own client.


Client

The proxy isn't very interesting until we create a client that will use it. In particular, we want a Flash client that allows us to demonstrate our ability to grab XML data from anywhere on the Internet, not simply within our own domain.

Transparency of the Proxy

Ideally a proxy is completely transparent to the application. In some situations this is the case; Many corporate environments, for example, use firewalls that function as proxies. These proxies are smarter than ours: They analyze the content and source of messages and actively filter them. They also manage error conditions far more gracefully. And they are invisible to all but the very lowest layers of Internet software. The users of a client-side socket are generally unaware that a proxy sits between them and the remote server with which they are communicating.

As we build a Flash-based client to work with our PHP proxy, we cannot achieve such transparency. Unless we are prepared to do some extravagant rewiring of the Internet, we must address the URL of the proxy and only indirectly the URL we really want. But we can come close to this ideal by wrapping up the proxy communication and hiding it inside a function. A sweet way to do this is to write an extension of the XML.sendAndLoad() method.

Extending the XML Object

Let's write an extension of sendAndLoad() called sendAndLoadRemote().We wrap the proxy in this new function, which mimics the functionality as well as the interface of the original sendAndLoad(). The difference is that our new function is not restricted by the Macromedia domain perimeter. (And it requires the presence of our new PHP proxy on the server.)

This is a simple but very useful function. We might wish that it were available in all our XML objects. It can be. We add this method to all our XML objects by installing it as an extension of the standard XML object's prototype.

ActionScript
XML.prototype.sendAndLoadRemote = function ( url, xmlResponse )
{
  xmlRequest = new XML("<xml><proxy-target>"+ url +"</proxy-target>"
             +"<proxy-request>"+this.toString()+"</proxy-request> </xml>");
  xmlRequest.contentType="text/xml";
  return xmlRequest.sendAndLoad( "xml-proxy.php", xmlResponse );
}

The first line in this function composes the message we will send to our proxy. It is a single XML element, as all XML documents must be. But this element can contain two daughter elements.

The required element, <proxy-target>, is a simple URL. It can contain the method prefix (http:) and the double slash, although our proxy will simply discard everything up to (and including) the double slash. It can include a GET style suffix (?someVariable=someValue) that will be preserved.

The optional element <proxy-request> is an arbitrary XML structure. In fact, it is the con tent of the XML object (this) that is performing this method. This element is optional from the point of view of the proxy. (GET requests, for example, must ignore it.) Since there is always a calling XML object, this option will never be undefined, but it will often be empty. This method is frequently called with a new XML object, and in such a case the request ele ment will be empty of real content.

Note also that this line of code is not simply composing a quick and dirty XML string by concatenating the appropriate fragments. We have done so in the past and used the resulting string directly as XML. Here, however, we are using the string as source code. The ActionScript parser is fed this ad hoc XML string and creates a genuine XML object in memory. We will see the importance of this on the third line.

ActionScript tolerates a shortcut we have taken with this code. If the XML object calling it has a prologue (the XML header, rarely seen internally, but always part of a formal validated XML), the prologue is supposed to precede the XML data proper (i.e., the header comes before the document contents). In our code, the prologue is produced properly by XML.toString() but then embedded incorrectly in the XML string, which should not compile. ActionScript assembles the final XML properly.

  • Prologue (from a WAP XML file)

XML
<?xml version="1.0"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
  "http://www.wapforum.org/DTD/wml_1.1.xml">

  • Malformed string fed to the tolerant ActionScript parser to make xmlRequest

XML
<xml><proxy-target>http://robots.net/rss/articles.xml</proxy-target>
  <proxy-request><?xml version="1.0"?><!DOCTYPE wml PUBLIC "-
  //WAPFORUM//DTD WML 1.1//EN"  "http://www.wapforum.org/DTD/
  wml_1.1.xml"></proxy-request></xml>

  • Proper XML as corrected by ActionScript and reported by xmlRequest.toString()

XML
<?xml version="1.0"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"
  "http://www.wapforum.org/DTD/wml_1.1.xml"><xml><proxy-target>
  http://robots.net/rss/articles.xml</proxy-target></xml>

The second line—

ActionScript
xmlRequest.contentType="text/xml";

—merely sets the content type correctly. Some older systems, including perhaps the ones which you are contacting by proxy, ignore (or even mishandle) the XML content type. But it must be set properly here. PHP requires it, and bear in mind that this header is intended only for the PHP proxy.

In the final line—

ActionScript
  return xmlRequest.sendAndLoad( "xml-proxy.php", xmlResponse );

—we wrap this function around the actual ActionScript data exchange method, sendAndLoad(). The necessity of creating a full XML object in the first line should now be clear. sendAndLoad() is a method of the XML object and the only way to upload XML data.

Calling this function from a new XML object with the URL http://bigfun.net/site.xml causes this to be sent to the proxy (whitespace added):

XML
<xml>
  <proxy-target>http://bigfun.net/site.xml</proxy-target>
  <proxy-request />
</xml>

Now we have a function that can be used with any ActionScript. But we need a demonstra tion program. A dramatic demo program is one that will allow us to display files from all over the Internet. To do this, we want to input a very popular XML file type.


RSS File

RSS is a simple XML format popularized originally by Netscape to describe the content of web sites. Aggregators, metasites, and search systems developed tools for reading these files, which was far more illuminating and efficient than analyzing HTML pages. In response to these tools, publishers began to highlight their content using RSS.

The acceptance of RSS snowballed beyond Netscape's expectation as it became particularly popular with publishers of ephemeral content. They saw RSS less as the index to a static site and more as a snapshot of the fleeting contents of a dynamic news source. Even this perspective has been undermined by a more XML-oriented point of view.

Publishers like the BBC, CNET, CNN, Disney, Forbes, Motley Fool, Wired, Red Herring, Salon, Slashdot, ZDNet, and so on see RSS channels as a way to pull in site traffic. The RSS newsfeed is a stream of hyperlinks that function like tiny free banner ads in the global competition for attention.

But for aggregators, XML is the stuff. The web pages with their human-oriented content serve only as backup for these gossipy chains of headlines.

For the meta-publishers, the RSS data is a newsfeed. They build pipelines of information flowing through channels that filter, package, and present these recombinant fragments of interest. These information merchants sell their point of view: the stories are a free commodity, the aggregators market branded discrimination.

The popularity of RSS is due in no small part to its simplicity. Unlike the extensible XML on which it is built, the RSS standard is remarkably closed. Its elements and attributes are (in the pop ular version RSS 0.91) strictly limited in number and size, arguably a problem for publishers but a boon to software developers: We need to consider only a finite degree of software complexity.

The RSS file has a single element called a <channel>. This <channel> has simple descriptive elements, most of them optional, and it contains a series of <item> elements.

An <item> is even simpler. It consists of a <title> (usually a headline), a <description> (usually a brief abstract), and a <link> (presumably to the original news source).


RSS Browser

Our application has two basic screens. One shows a selection of URLs, each pointing to an RSS file, and allows the user to choose one (or supply a new URL). The other screen opens the URL and examines the channel that it returns.

URL Choice Screen

First we construct a little MovieClip called a ChannelPick (Figure 18.3). It is used to display, edit, and choose a single URL. It presents the variable url to the user as an editable line of text. Of course, url is specific to the namespace of this MovieClip instance, so a screenful of ChannelPick instances will not interfere with one another.

Figure 18.3. ChannelPick: Button and Editable URL

ChannelPick also has a simple button (Go to) that triggers the select() function. This copies the local value of url to the variable channel one level up and then runs the animation on that level.

The _parent MovieClip has two states. Initially it displays 16 ChannelPick instances and assigns a different URL to each url (Figure 18.4). (We supply a broad assortment of RSS publishers.) The action stops and waits for one of the channels to be picked by the viewer. The second state displays the chosen channel. It displays the channel variable (a URL), a back button, and an instance of the ChannelView MovieClip. It passes the URL to the getChannel method in that instance.

Figure 18.4. Seventeen ChannelPick Choices

Note that the second state is on the third frame, not the second. If you were to try to call a function within a MovieClip on the same frame in which you first instantiate it, the Flash player would not find the function. So here we wait a frame to allow the instance of ChannelView to realize itself before we try to address its internals (Figure 18.5).

Figure 18.5. Channel Viewing State

This frame also includes a back button that returns us to the first frame. It is not accidental that this causes the ChannelView instance to go out of scope. We want to destroy the instance and release all its data structures to the garbage collector. This is a cheap and easy way to do so. Each time we view a channel, it will be with a fresh instance of ChannelView.

ChannelView

This object has three simple display-only dynamic text fields. The fields present the title of the name of the channel, the description it supplies of itself and a status display that shows the progress (or problems) of the XML exchange.

We establish our proxy-based send and load as an extension to the definition of the XML object. (The actual function body code has been examined earlier.)

ActionScript
XML.prototype.sendAndLoadRemote = function ( url, xmlResponse )
{
. . .

The function getChannel grabs the XML from the remote server. It sets up the status dis play and then calls new XML() twice to establish two fresh, new XML objects (xmlRequest and xmlResponse). It is often cleaner to use a single object for both purposes, but here clarity is more important than efficiency.

ActionScript
function getChannel( url )
{
  statusline="WAITING for XML";
  xmlRequest = new XML("");
  xmlResponse = new XML("");

After setting up the status display, the getChannel creates two new empty XML objects. It prepares xmlResponse to receive data, then exchanges an empty xmlRequest for an xmlResponse from the server.

ActionScript
  xmlResponse.onload = showMe;
  xmlResponse.findElements = findElements;
  xmlResponse.target = this;
  xmlResponse.sendAndLoadRemote( url, xmlRequest );
}

As in Chapter 14, we add two new methods and a property to the xmlResponse object, which will contain the downloaded data. The showMe function is registered as the onLoad callback. An introspective method called findElements is installed. The target property hooks the XML object back to its mother MovieClip and allows it to address the functions and variables that directly affect the display. The keyword this, in the penultimate line of the code, refers to this instance of the ChannelView MovieClip, not this instance of the XML object. A bit tricky perhaps, but bear in mind that these lines are MovieClip code—they are building an XML object from the outside, not executing within the XML object.

Another point to consider is that we could have extended the XML object with submit and findElements, as we did with sendAndLoadRemote, by writing it into the XML prototype. This is often more efficient than duplicating the code in many instances. But not here. Although we have a few XML objects, only one needs an onLoad function or the findElements method. These are specific to the incoming RSS file, and it is more appropriate to install them, as we have, in this single instance than as a general XML property.

ActionScript
function showMe( ok )
{
  statusline= ok? this.toString() : "Server not found"+this.toString();
  this.findElements();}
}

This onLoad handler is quite simple. It shows a text version of the incoming XML in the background. (If there is an error in the link between Flash and the proxy, showMe prints its own error message.) Then it launches the findElements() introspector.

ActionScript
function findElements()
{
    if( this.nodeName eq "channel" ) this.target.rssChannel(  this)
    if( this.nodeName eq "item"    ) this.target.rssItem(     this)
    for( var i=0; i<this.childNodes.length; i++)
    if( this.childNodes[i].nodeType ==1 ){
       this.childNodes[i].target      = this.target;
       this.childNodes[i].findElements= this.findElements;
       this.childNodes[i].findElements();
       }
}

There are only two elements that findElements can recognize, the <channel> and the <item>. The relationship between them is pretty fixed (the file has a single channel, the channel contains 0 to 15 items) and so is their position in the XML file. But it is still easier and more robust to write a generic findElements() function that recursively scans the element nodes of the entire tree.

When it finds either a channel or an item, it calls the appropriate function back in the MovieClip instance.

ActionScript
function rssChannel( element  )
{
  for( var i=0; i< element.childNodes.length; i++)
      if(      element.childNodes[i].nodeType== 1)
         if(      element.childNodes[i].nodeName== "title")
              this.title = element.childNodes[i].childNodes[0].nodeValue;
         else if( element.childNodes[i].nodeName== "description")
              this.text  = element.childNodes[i].childNodes[0].nodeValue;
}

The channel decoding function has an easy job. It looks through all the child nodes of the channel, and it ignores everything except <title> and <description> nodes. We are careful, in this case, not to use a recursive function, because titles and descriptions are also found in the items within the channel. We want only the channel's own title and description. When the title or the description is found in an element node, we (somewhat boldly) assume that the node immediately below this one will be the matching CDATA node (the text). We assign that text to variables that are displayed on this screen.

An <item> element requires a bit more effort. For each one we find we spawn a new instance of the ItemView MovieClip. Each of these instances requires a (locally) unique name, a globally unique depth, and an x and y position:

function rssItem( element  ){
    this.attachMovie( "ItemView", this.i, 1500- ++this.i);
    clip= eval( this.target + this.i );
    clip._x= this.xchild;
    clip._y= this.ychild;
    if( (this.ychild+=20) >160) { this.ychild=0;this.xchild+=150;}
    clip.item= element;
}

A simple counter this.i provides both the name and the depth (notice that we want them to stack visually behind one another, so we use negative i. Note also that in an expression like ++this.i or 1500+this.i the + is an arithmetic operator and this.i is a number. However in this._target + this.i the same symbol is the concatenation operator and this.i is a string .

Depth, x, y, and name are counted off by variables in this, which makes them local to the ChannelView. Thus the variables persist across calls to rssItem and the ItemView objects stack nicely and are named neatly. The variables are reset when ChannelView is destroyed and recreated (when a new channel is being viewed).

The clip variable is just a temporary variable within this function that we use to address the properties and variables of the new ItemView. The final line sets one of these variables, item, to be a copy of the XML description of the item. This pushes the decoding of XML data even deeper into the application. In fact, the ItemView contains the most interesting implementation of RSS functionality.

The ItemView has only three parts: a title, a description, and a link. We build these into a single object that shows only the title. When the mouse rolls over it, the description is presented. And when the mouse is clicked, the link is opened (in a new window).

This sounds like a fancy button, and the easiest way to implement it is by attaching a button (which provides nice predefined states) to a MovieClip that provides the unique namespace we require (Figure 18.6). (Since multiple items are viewed at once, all the buttons would share namespace and be indistinguishable.) The button is not complicated. It consists of text and flat backgrounds for the text. Different variables are displayed in the three states. The variables for the text are title and description. These (and the mouse action) are defined in the ItemView MovieScript that wraps around the button (Figure 18.7).

Figure 18.6. The ItemView Displays as

Figure 18.7. The Item Acts as a Hyperlink

On frame 1 nothing happens. If we were to try to access the data in this MovieClip on the first frame of its existence, we would be disappointed. The data is placed there by the parent object, but this instance is created (and its initial frame is executed) before the data assignment happens. They happen on the same frame, but not simultaneously.

On frame 2, we have all the muscle:

ActionScript
function setVariable( node ) {
   name= node.nodeName;
   value= "";
   for( var i=0; i< node.childNodes.length; i++)
      if(      node.childNodes[i].nodeType== 1)
        setVariable( node.childNodes[i] );
      else if( node.childNodes[i].nodeType== 3)
        value += node.childNodes[i].nodeValue;
    eval( name ) = value;
}

This recursive function is used to set the title, description, and link variables. In fact, it will create any variables it finds. Whenever it finds a named element, it creates a variable of the same name and assigns to it the value of all the concatenated text nodes beneath it. If the RSS file is valid, this function will always fill in the title, description, and link that will make the button displays function.

setVariable( item );
stop();

Now our little browser is ready to run (Figure 18.8). It needs to be in the same directory as our PHP proxy. That proxy needs to be named "xml-proxy.php" or whatever is called by the function XML.prototype.sendAndLoadRemote();

Figure 18.8. A Channel Displayed in the RSS Browser

To work the browser, a user simply clicks on a channel that seems interesting. The items in that channel can be examined, and underlying pages can be browsed. Try the preprogrammed channels, or type in the URL of any RSS channel.


Conclusion

We have begun to develop socket programming skills. We have created a simple set of HTTP functions to allow us to build an XML proxy server. With this proxy service, we can jump over the fences Macromedia has put around the Flash player.

Once we had the proxy service, we were free to explore the rapidly growing worlds of XML content sharing. We examined one of them, the world built around the RSS standard.

Using XML as a technique for communicating between tiers of your own application is exciting, but it is merely scratching the surface. The power of the format becomes really exciting when you participate in a worldwide web of creativity and content.


Chapter 19. XML Sockets

In this chapter we learn to create a full socket system connecting our own remote server and our own Flash clients.

The last chapter presented a fairly elementary introduction to sockets. We built an XML proxy using fsockopen(), a simple function that treats the socket system as an input stream. This allows us to use the familiar methods of file i/o to create simple client sockets in PHP.

In this chapter, we return to the same topics and examine them in far more depth. We now use PHP to build server sockets, which are far more demanding and cannot be cast as file operations. We also build client sockets in Flash. We create a single client/server socket connection and a live test program that lets us send data on the round trip.

This chapter is of interest to those who need to build low-latency, persistent connections between user and server. A single-user application would typically involve a streaming data feed, such as a market monitor or some sort of remote real-time instrumentation.

The study of sockets is interesting, but it is difficult. The code samples are problematic. They do not require a privileged account, but they do demand the cooperation of your system administrator.

The functions in this chapter can cause real problems for yourself and for other accounts on your server. Permanent damage (e.g., file system corruption) is unlikely, but experimentation with the functions might result in service interruption or a bandwidth overload. Cleanup might require superuser assistance.


Advanced Socket Programming

Socket is a Unix-derived concept for an active data connection between two processes. Terms like "socket" and "port" were chosen to give this construct a very physical and simple sense. But it is neither physical nor simple. It is a complicated software abstraction that encapsulates layers of even more complicated networking protocols and the artifacts of transmission hardware.

The simple promise of a socket is that information pushed into the socket by some application will appear in another application that is attached to that socket. In a two-directional socket stream, like the one we will develop here, the data flows in both directions.

The two programs that are linked together like this might be on the same computer, they might be joined on a LAN, or they might be linked by the Internet anywhere on Earth. Except during the initial step of establishing a connection, these differences are invisible to the socket user.

Our previous work on sockets led us to implement a web client in PHP. Client-side programming—especially creating a little browser or robot for the web—does not demand much control over the socket.We used the fsockopen() function to create a socket connection with the well-understood behavior of an open file. It seemed pretty easy, because some other programmer did the hard work of building the socket functionality on the server side.

Server-Side Socket

Server-side sockets use full-duplex bidirectional connections (meaning that both client and server sides can talk and both can listen simultaneously). But they are not symmetrical: The server side has host responsibilities to observe. Since the client initiates the contact, the server has the more difficult job of waiting. The host is always listening for service requests.

An even more dramatic asymmetry is the fact that a client attaches to a single server, but the server is managing requests from many clients. An actual socket can connect to only a single client, but a server often needs to respond to hundreds of simultaneous requests.

One socket, identified by IP address and port number, is the published location for a given service. All requests address this socket. This means that the server must manage a system of many active virtual sockets, each behaving as if it were the single public socket known to the clients.

IP Address

Actual server connections are made to IP addresses. These are represented usually in the form 123.234.12.34. A 32-bit address (4 bytes) is displayed as four decimal values (0–255) divided by dots. This full number is used to address a single machine somewhere in the universe. The IP address of a web server, like most hardware that is publicly connected to the Internet, is a static IP address: Its 4-byte address will be the same tomorrow as it is today.

In the case of a dial-up Internet connection, the ISP (Internet service provider—the company providing the connection) generally lends the user an IP number from a pool of numbers that the ISP owns. Even in always-on connections, such as DSL or cable modems, the user is given a dynamic IP address. This technique means that a user's actual address changes frequently. It is roughly equivalent to the phone company giving you a new phone number every time you make a call. It is easy for you to make a call, but it is extremely hard for anyone to call you, since you have no number to publish or even give to your friends.

A dynamic IP address makes it difficult to implement a server at a network node intended for surfing with a browser. Inventive minds still find ways to do it (like peer-to-peer music sharing), but it is tricky and it presents interesting business, political, and cultural issues as well as the simply technical ones.

Domain Name

It is rare to know a server by its numeric address. We know its domain name instead. We look for awl.com, not 204.179.152.52, though they both resolve to the same machine. The domain name is a handy abstraction both for mnemonic reasons and for the freedom to dereference: Someone who is looking for Addison-Wesley content looks for the Addison-Wesley domain and doesn't worry about where the content is actually served from. Easier for them to remember, easier for AWL to change to another server.

To open a socket, we need the IP number, not the domain name. The tables that translate between the two are distributed throughout the Internet. The domain name server (DNS) is a process that delivers the IP address for a given name.

To use the domain name service from within PHP, we need to know not where it is located or how it works but just the PHP function call:

PHP
<?  $address = gethostbyname ( "awl.com" );
     echo $address; ?>

yields

Output
204.179.152.52

In ActionScript the task is even easier. The XML.Socket object has a connect() method that will accept either a numeric address or a named domain. It alternatively accepts the keyword null, which specifies a connection to the server from which this movie was downloaded. Because of the security restrictions on all Web-delivered Flash applications, this is the only domain they can in fact connect to, so the null parameter is by far the most common.

ActionScript
myXMLSocket.connect( null, 12353 );

The server side is similar. The server we build uses a port on its own machine. This derives not from any arbitrary security restriction but from the most fundamental definition of the client/server model.

PHP reserves a name that identifies the server on which we are running:

PHP
$address = gethostbyname ($SERVER_NAME);

It gives us a usable numeric reference to the current server.

Port Numbers

A port number is an arbitrary number that functions like the extension portion of a business phone number. A PBX phone system allows all the telephone users in an organization to share one public number by routing each incoming call to the proper extension. The port number allows the many processes on a modern computer to share the single IP address; incoming messages are routed to the proper process.

In practice, a publicly known port number is usually identified with a service, not the process that performs a service. To continue the phone analogy, it is as if job applicants were told, "Call extension 202 for human resources."

Most of us are familiar with the ports on a personal computer. Serial ports (like COM1:, COM2:, and so on) usually identify actual UART chips, Even printer ports (LPT1:, LPT2:), which are somewhat more abstracted, still resolve quickly to a specific hardware connection. Forget all this. The Socket port has no relation to installed hardware. Its limit is simply the mathematic range of a 16-bit integer.

Unlike the domain name situation, port numbers are not controlled by a global authority. This makes sense since port numbers are within the domain of a single machine. Within a machine, the assignment of port numbers is typically controlled by the system administrator.

To implement the example projects in this chapter, you will need your own dedicated port number. Generally this requires you to request the assignment of a fixed number from your system administrator. Our examples assume a Unix server, although Windows also has robust Socket support.

By convention, Unix port assignments follow this pattern:

0–1023 | Privileged processes
1024–5000 | Ephemeral processes
5001–65535 | User processes

PRIVILEGED PROCESSES

These classic services of a web host have port numbers that are validated by decades of history. Most are odd numbers, since they date back to an era when a pair of ports (conventionally consecutive) were required for bidirectional communication. Some of the well-known services with dedicated ports are the following:

• tcpmux | 1 | TCP Port Service Multiplexer
• ftp-data | 20 | File Transfer Protocol (Data)
• ftp | 21 | File Transfer Protocol (Control)
• telnet | 23 | Virtual Terminal Protocol
• smtp | 25 | Simple Mail Transfer Protocol

The privileged processes are given port assignments from 0 to 1024. To bar collision between user-built code and these privileged services, Macromedia has installed yet another security restriction into XML.connect(). It does not accept a port number below 1024. (Most systems would not allow anyone without root privileges to create a service with such a port number.)

But why restrict the client? It might prevent a sloppy programmer from accidentally stepping on the ftp service, for example, but it also prevents an ambitious one from building (for example) a Flash-based telnet client or a mail program that uses Flash graphics and interfaces directly to an SMTP server. Neither of these applications seems easy, but they would both be interesting.

EPHEMERAL PORT ASSIGNMENTS

A TCP communication that requires its own dedicated port generally must be assigned this port temporarily by the operating system. We will soon be building such a system and we will be asking the OS to assign us ephemeral ports, in addition to the dedicated port assignment we request from the system administrator.

Assignment of an ephemeral port to a requesting process is fleeting, and the port can be swiftly and safely reassigned to another process. A band of addresses from 1024 through 5000 is reserved for these ephemeral ports. In Linux systems, this convention seems to have been violated and the band of ephemeral ports runs from 1024 through 32767. This may reflect the growth in importance of TCP which results in a huge appetite for ephemeral ports in the sort of large-scale web servers that Linux is used for.

USER PROCESSES

All the remaining numbers (even in Linux they are plentiful) are available for individual processes. The port number alone suffices to invoke the special process. For example, at one time we used bigfun.net:8383 to invoke the semipublic mail service at Big Fun. The requested port number appears at the end of the formal URL, separated by a colon. A default port number (in the privileged process range), such as http: (80), ftp: (21) and smtp: (25), is assumed for many protocols.


XMLSocket

Even in ActionScript, building client-side socket software is not terribly difficult. Remember that ActionScript has two different objects, XML and XMLSocket. These are entirely distinct. An XML object is a data package. It has many properties and many methods, but they all deal with a tree of data that is organized along the principles of XML and that can translate itself from string representation to data object and back with great reliability.

An XMLSocket is not a data interface at all; it is a communications interface. The operative word is "socket," and its methods are similar to the socket function in PHP, Perl or Unix.

ActionScript Methods

Even when compared to a fairly dumb PHP socket function like fsockopen() that provides limited client-only control, the ActionScript methods seem quite simplified.

connect( host, port)

This method begins the process of connecting to a remote socket. The default host is the one from which the swf file was served. In the case of browser-based swf files, the host must be in the same subdomain as the source of the swf, so this default is nearly a requirement. (The default is requested by a null host parameter, such as ""or the keyword null.) In fact, experimentation reveals an even simpler fact: You must use null. Correct identification of the server (either by full name, domain name, or by numeric IP address) is not recognized properly by Flash. If you request a connect to your own server by naming it, Flash refuses the connection. If you use the null parameter it succeeds.

This is quite disappointing. The domain perimeter security feature is generally an impediment to the developer. But here it could be a great boon. If a domain name is hardcoded in a Flash connection, the application will communicate properly only when it is hosted in that domain. This would make the .swf file inoperable if it were swiped by a pirate webmaster. The domain perimeter principle can protect a publisher's intellectual property, but current implementations fall short. The identification of the host is relative, not absolute.

The port address must be known by Flash, either by some global convention or by an arbitrary installation-dependent choice (is typically the latter). Hardcoding this port assignment into Flash code may seem reasonably expedient, and often it is. However, a conscientious programmer might allow his client to ask the service for a port number before launching the socket. This would complicate the process and require two separate mechanisms. First, an HTTP connection, presumably using XML.sendAndLoad(), enables the Flash client to request a port number from the server. The second phase of communication starts with XMLSocket.connect() directed to that port.

Keep in mind that socket connection is an asynchronous process. It is not unusual for the connection to take a few seconds to establish. In many active Flash displays, a pause of a few seconds is as subtle as a car crash.

So this function only initiates the connection process. It returns immediately with true or false, but these values cannot be relied on for much. In fact, the return value tells us only whether ActionScript even bothered to attempt a connection. A false means that our parameters failed ActionScript's two basic gate-keeping rules: either our port number was below 1024 or our host parameter (in the case of web-based Flash) was non-null. A true result does not mean that the domain exists or that the port number is legitimate.

onConnect(success)

This event handler allows us to complete the asynchronous connection. We can install a callback function to manage the outcome of the XMLSocket connection. The function we install is called by Flash when the connection is established or when the software times out unsuccessfully. (Flash does not allow us to tweak the number of retries or otherwise control this timeout.)

The function that is installed here should have a single boolean parameter: success. If success is true, it is time to fire up the socket activity we have planned. If unsuccessful, there needs to be (at a minimum) decent error reporting and perhaps even some sort of error recovery.

close()

This is the method by which an XMLSocket closes itself. The object itself is not destroyed, and it can be used to connect again. Use delete() to remove the XMLSocket object.

onClose()

There would seem to be symmetry between the connect/onConnect and the close/onClose methods that initiate and end a socket connection, but there isn't. True, connect() starts an asynchronous process that ends with onConnect. On the other hand, close() starts an immediate process. It does not cause onClose() to occur. Rather onClose() is invoked only when the XMLSocket is closed involuntarily. An outside process, most likely the server itself but possibly a communication break, causes the socket to close.

If we have installed an event handler, it is invoked by such breaks in the socket connection and gracefully manages the situation.

send(object)

This is a method of the XMLSocket by which it sends an XML object across the connection. Note that the XMLSocket is not sending itself across the connection. As we considered earlier, this object embodies the connection, not the data that goes across. The XMLSocket is a socket; it is not XML. In design, it is a socket through which XML objects can transparently pass. This send() method will serialize the object, that is, convert it to a stream of text, using the XML object's toString() method.

In fact, the interface permits sending other data, as well. Strings are sent as is, and other data is serialized as well as possible. But this fact tempts us to create quick shortcuts that never seem to be updated. So we will forget this aspect of send() and concentrate on building honest systems for two-way transparent XML communication.

Note that send(), like connect(), is asynchronous. The call simply initiates the action. The actual performance occurs later. Unlike connect(), there is no callback function that tells us the results of the send() action. We send() our data—and hope it gets there. In fact, it is only by observing the behavior of the application that we can tell if we succeeded or not.

onXML

This function handles the XML data received event of the socket. Unless we write and install a callback function, nothing will happen when data arrives across the socket. Rarely is this the desired behavior.

It is important to remember that the XML data received event does not happen when data is received. It occurs when an XML object is received. Specifically, it requires a complete, closed XML document terminated by a null byte.

Since packet transmission is fairly independent of the application code, it is not unusual for a data message to span several TCP packets or for one packet to hold several data messages. In the former case, the XML object is stitched together; in the latter case, the objects are individually broken out.

The parser is given each null terminated string to parse, and then it calls the onXML function we have installed. The system creates a legitimate XML object and passes it to the application as the parameter of the onXML function. It is the job of onXML to pick apart the nodes and do something interesting with the new object.

Caution

In our examples we need to be attentive. PHP does not automatically punch out XML documents; the opening and closing of tags is entirely our responsibility. (Opening and closing tags is not terribly hard, but often it is a challenge to nest the elements and wrap the response in a single element, a requirement of legitimate XML documents.)

Our attention is especially required because PHP does not naturally traffic in null-terminated strings. Note that in our code we explicitly append \0 to the end of each string we create in PHP. Coming from the other direction seems simpler, but it is also error prone: ActionScript sends null-terminated strings to the server. If we ignore the nulls, they do not disturb the PHP strings—they are invisible. But in a chat system, for example, strings are churned around and passed back and forth from clients to server. If the nulls are not stripped out, they soon get mixed into the strings. Though benign in PHP, they break up the strings in ActionScript and spark recombinant chaos that is rarely fun or productive.

Using XMLSockets

The XMLSocket concept is fresh and attractive. It sparks the imagination to create independent entities driven by little engines of connectivity to some outside work process. Even the most obvious opportunities are lively and exciting: a chat system, a live stockmarket ticker, and so on.

In many cases, XMLSockets can be a promising design choice, but be cautious. XMLSockets are not easy to establish and maintain. And it is tempting to create specialized XMLSocket objects for many components of an application. Each socket can exhibit different behavior when data is received; the sockets might share behaviors, as well, both standard and prototyped. While this is a clean, sturdy, object-oriented architecture in which each object maintains an independent link to the outside world, it is a little too academic for the real world.

Though sockets are simply abstractions, they do exact a finite cost. Each socket that is supported chews a bit of resources out of allocation on the remote server. Such an expense could quickly get out of hand in a design when many game characters, for example, are each driven by an individual socket connection.


Simple Socket System

Following is a very simple demonstration program that will send a fixed-length string to a server and receive the same string echoed back. We create three XMLSocket callback functions to handle onOpen, onClose, and onXML events. A fourth function is attached to a submit button. This submit() function packages the input text into XML and transmits it to the server using XMLSocket.send().

ActionScript
socko= new XMLSocket();

Since this is a simple exercise, we make and modify a single instance of XMLSocket. A more ambitious project might extend the XMLSocket object by creating new prototype functionality.

ActionScript
socko.obj = this._target;

The methods we create need to address the namespace of the MovieClip which is creating this socket. We create text entry and display fields that manage the strings we are sending and receiving. The onscreen text fields and the XMLSocket routines communicate through variables in the clip's namespace. The obj property is our own device. It allows us to easily access these MovieClip variables as, for example, obj.status or this.obj.textOut. The namespace is inserted into the XMLSocket object as its obj property.

portno | target port, set by default but overwritable for convenience
status | socket communications status report
textIn | latest message from the server
textOut | line being composed for uploading

The first frame of the socko MovieClip has a single simple line of code.

ActionScript
status ="";

All it does is clear the status line, so if we make repeated connection attempts, it removes the previous result and stays neat. (We would make a second attempt by typing in a new value for portno and clicking the button near it, which calls gotoAndPlay(1);. This, in turn, will execute this code and clear the status display.

Figure 19.1. A Primitive Chat Client

Frame 2 has this bit of active code.

ActionScript
if (!portno) portno= 12538;
status += socko.connect( "", portno )? " [Connect":" [Connect FAILS ] ";
stop();

It sets a default for the port, if the user has not, and it attempts a connection. Note that it connects to "", which means the server from which it was downloaded. The only way this will fail initially (and give the FAILS message) is if the port is below 1024.

But we will code like this, hoping that ActionScript will continue to become more sophisticated and that this kind of test will be more useful.

ActionScript
socko.onConnect = function ( ok ){
      status+= ok?  "ed] ":"ion Not Completed] ";
      }

This code completes a phrase begun when the XMLSocket attempted to launch a connection. When it began to contact the server, it printed on the screen's status line: "[Connect". Now, depending on the outcome, it becomes "[Connected]" or "[Connection not Completed]".

It is good that this error message is different from the message we print in the case that connect() itself returns a failure code.

ActionScript
socko.onClose = function(){
      status+= " [Disconnected by server] ";
      }

This message is an important status message. It comes when the connection is severed by the server, probably at a time when the user does not expect it. (A nice improvement here would be the addition of an appropriate alert sound.)

ActionScript
function transmit()   {
      textOut+="                                    ";
      textOut= "<msg>"+textOut.slice( 0, 36)+"</msg>";
      socko.send( new XML(textOut) );
      textOut="";
      }

This function pads the output line with 36 blanks, then trims the result to a total length of 36. When the tags and the terminating null are added, the data package is exactly 48 characters.

While we generally prefer more flexible code, we need to work within the restrictions of the PHP and ActionScript routines. We found that we saved ourselves an extraordinary amount of pain (and kept this demonstration far cleaner) by restricting our data package to a fixed size.

ActionScript
socko.onXML     = function (msg) {
      textIn    = msg.toString();
      }
Figure 19.2. Packet Containing an XML Fragment

Rather artlessly, we are going to simply display the incoming XML document in its entirety. This is tolerable, since we know it will be short and simple, but a more mature function would isolate the text from the markup. It would try to display the former formatted by the latter.

All we need now to complete the client is to hook the second button (and the Enter key) to our transmit method. We click on the button instance and add this code to its object actions.

ActionScript
on (release, keyPress "<Enter>") {     transmit(); }


Simple Socket Server

We start coding the socket server with a few pragmatic concerns.

HTML
<html><head><title>SERVER OF SOCKETS</title></head><body>

It is good to have a clear title on the window. When we are debugging a multiple-process system like this, many windows are open at once. It is important to easily identify the significant windows even in a crowded display.

Unix Error Codes

PHP
<?php
function check( $value, $function )
{
   if( $value >= 0 )
        print( "\ n<br/>function [ $function ] returns $value.(success)");
   else
        die  ( "\ n<br/>function [ $function ] returns $value.
                   (<I><B>FAILURE:</B></I> --".strerror($value)."---)" );
    superflush();
}

This check() function simply handles our response to the many system-level function calls we make in this program. Some return integers as handles or lengths. Others use zero as a return code to indicate success. But they universally use negative numbers to indicate errors and they share a single mapping of negative numbers to error messages.

Throughout our software we call check() with the result (and name) of a critical function as it is performed. If things go well, we get a success message on our browser—which is serving as monitor. We use both <br/> and \n to force a newline in both HTML and a straight dump. We use print() rather than echo because we can flush its output buffer. Nevertheless, a more powerful flush is required, called superflush().

If there is a problem, it is reported and the program aborts (die provides both functions). The function strerror() translates a numeric errorcode into a human-ready error message.

Buffer Control

function superflush()
{
  for($i=3;$i;$i--){
       for($j=5;$j;$j--)
            print("                                         ");
       flush();
       }
}

This is not very lovely. Neither is the rubber plunger standing near a balky drain. But both are necessary tools.

The PHP call to flush() was developed to override the normal buffering of the web server system that causes output to accumulate before it is transmitted back and displayed at the client. Normally, the buffering makes the system more efficient. But it delays the display of tiny fragments such as the debugging and progress messages we are looking for.

Ideally, a single call to flush() would be enough to push the output we want to the screen. But there are several steps between the code and the client screen where buffering is performed. Pushing a lot of inert whitespace through the system helps clear its buffers.

Trial and error proved that this brute force superflush() is required. Its details are definitely system dependent. Some systems need only the normal flush(). Others might need an ultramegasuperflush().

HTTP Channel

This flush is used to clear the HTTP channel (the normal browser/server web channel), not the socket channel that directly connects Flash and PHP. Right now we use the HTTP channel only to open a browser window for development purposes. Using simple HTML, we can monitor the PHP program as clients connect and communicate.

Later we might use the channel more strategically. For example, we discussed how the HTTP channel could provide a Flash client with the port number of the desired service. This lets us keep the port number pretty dynamic, both from installation to installation and on the same machine.

The HTTP channel can serve to launch this whole process. This socket server is an ordinary PHP script and is launched in the ordinary way by the web server responding to a normal HTTP request from a browser or a Flash app. Using a simple browser, we can launch and monitor the server remotely across the web. Or we can let our Flash users automatically fire up the service as needed.

Cleanup

PHP
function closedown()
{
global $sock, $msgsock;
    echo( "</body></html>" );
    close ($msgsock);
    close ($sock);
}

PHP does not use many event handlers or callback functions. That is a shame in this case—it will be clear shortly that the event-driven socket code we wrote in ActionScript is a far better system than the polling loop we will code in PHP. The event-driven code is cleaner, and the software performs more efficiently and reliably.

We do have a callback function that handles cleanup when the PHP script terminates. This cleanup code originally simply sat at the end of the script. By registering it as the shutdown function we can be sure that it is also called when the script is aborted for any of a variety of reasons.

Time Limit

PHP
  set_time_limit(120);
  register_shutdown_function ("closedown" );
  $address = gethostbyname ($SERVER_NAME);
  $port =    12539;

One reason for the script to abort is a time-out. We give this script a 2-minute (120-second) time limit while it is on the workbench. In actual service, we will want a longer (or unlimited) time, but while we are developing code that can monopolize system resources, we want a safety valve to prevent a meltdown.

Figure 19.3. The Two Steps of a Client/Server Connection

The final lines install our cleanup code, retrieve our IP address, and choose an arbitrary port number.

Listen Socket

PHP
  print( "Opening a socket at <b>".$address.' : '.$port."</b>.");
  check( $sock =      socket(AF_INET,SOCK_STREAM,0),  "socket" );
  check( $ret  =      bind  ($sock, $address, $port), "bind"   );
  check( $ret =       listen($sock, 5)              , "listen" );

The listening socket, $sock, is built in three steps:

  1. The function socket() establishes the handle for a socket and allocates the data structure that will manage it. The AF_INET parameter specifies an Internet socket (as opposed to a Unix socket for communication between local processes). SOCK_STREAM specifies a data stream rather than several more primitive and fragmented options.

  2. Function bind() associates our named handle with a unique network address. It fails if that host/port/protocol combination is already in use. If we are sloppy in our allocations and shutdowns, this will be a problem. We can leave ports unavailable even after our code has completed. This is especially a problem during development.

  3. Finally we tell $sock to listen to the port. The listen() function does not wait for data. Rather, it waits for connection requests. That is the sole purpose of this socket ($sock): It lingers at this port and welcomes incoming clients. We are, in this case, allowing it to build a queue of up to five pending clients.

These requests must then be processed.

Accepting a Connection

PHP
set_nonblock ($sock );
 while( $msgsock<1 ){
   echo strerror( $msgsock )."<br/>";
   superflush();
   $msgsock =   accept_connect($sock);
   sleep(1);
   }

By setting nonblock mode on this socket, we know that the execution of our script will not stop when we call accept_connect(). Were it in blocking mode, the script would wait for the accept_connect() action to complete. If nobody is waiting to connect, our process comes to a halt.

Our example has little practical advantage over a blocking strategy. The behavior is similar, but our nonblocking example is a little more lively and its action can be more visible. While it loops around waiting for accept_connect() to be satisfied, the script can print a status message (Figure 19.4).

Figure 19.4. Waiting for a Connection, Verbose Style

The significant line of code is the one that creates the new socket $msgsock by accepting a connection from $sock. These two sockets are not equivalent.

The original $sock socket goes back immediately to its job of waiting for new customers. In this single-user service this is not significant. In multiplexed systems, it will generate more message sockets to maintain more client connections.

The new socket $msgsock is associated with a particular user of the service, whereas $sock is associated with the public interface to the service. Unlike $sock, $msgsock conducts message traffic. We cannot use $msgsock to listen for new connections, and we cannot use $sock to transfer data.

Message Socket

PHP
  set_nonblock ($msgsock );
  do {
    $buf="";
    $readerr      =  read( $msgsock, $buf, 48);
    if( $buf )
        $writeerr = write( $msgsock, strtoupper($buf), strlen($buf) );
    }   while( !stristr( $buf, "quit") );
  closedown();
?>

The actual web service happens in this loop. We put $msgsock to work. It is not dramatic work. This example is just an echo service: It repeats back to the client each string it receives from the client.

The nonblock mode means that the loop progresses quickly. If there is data to be read, it is read; if not, the operation is skipped.

The buffer is emptied, and we attempt to refill it with a read operation. If the buffer has been filled, it is written back to the socket. To leave our process's footprint on the string, the script forces the text to uppercase.

Finally, we actually examine the user's message. If the user writes "quit", we do (Figure 19.5).

Figure 19.5. Disconnection

NOTE

At the time of this writing, we were using a version of PHP (4.0.4) where the nonblock read function was broken. Our software still worked without this feature but awkwardly. It made some compromises attractive, such as the fixed-length reads.

Later, in our example of multiplexed sockets, it made our software very clumsy. This bug in PHP was known and has been scheduled to be addressed, so it is hopefully now corrected and we can perform socket reads in nonblocking mode.


Conclusion

XMLSockets are a flexible technology for sharing live two-way data with a web service. The process we have sketched out is just a beginning. The technology and its application can both go much further.

In the next chapter we multiplex our XMLSockets, which not only makes them more scalable but opens up the new world of communcation between clients.


Chapter 20. Multiplexed XML Sockets

In the last chapter we built a system on XML sockets. We created a server-side application that accepted a connection request and opened a new socket. This new socket connected to a Flash XMLSocket on the client machine. This gives us a two-way connection between client and server. Either can initiate or receive a message. The ActionScript side was more authentically event- driven, while the PHP actually polled the socket.

In this, our final chapter, we extend the software to make a multiplexed server. This is a server that manages multiple simultaneous clients. A server like this is necessary, of course, just to scale the previous project to commercial strength. A system that requires a dedicated server port for each client is generally not ready for public deployment in the Internet. (There are a few exceptions with extremely limited traffic.) So multiplexing our server is important just to provide good single-user service.

The excitement comes when these clients are not treated in isolation. When the server passes messages from one client to another, we have entered a new type of web experience. Visitors are in direct contact with one another. Both one-to-one relationships and many-to-many architectures have use in entertainment and functional web sites. The power of a growing community and the real-time creativity of individual visitors are a great source of self-renewing content .

Many games and other projects can be started with this technology. We choose as our working example an application of great simplicity and enduring interest: chat.

These techniques, used carelessly, can cause an interruption of your web service. Depending on your situation, they also might impair the function of other organizations with whom you share resources. You may be unaware of these shared resources, of these organizations, and of the potential consequences of a service interruption.

It is irresponsible to experiment with these functions unless you are competent in the technology and can be certain that you are protecting yourself and respecting the rights of others.

NOTE

As noted previously, version 4.0.4 of PHP (current at the time of this writing) cannot do proper nonblocking reads. With multiplexed sockets, it made our software very clumsy. This bug was being corrected at press time.


Chat

Among the exciting possibilities of XMLSockets in Flash is that, with the right server code, two or more clients can connect to the same server at the same time and communicate fairly directly with one another. Chat is the obvious and ever-popular use of this technology. Head-to-head games are another exciting use. Certainly the combination of Flash's multimedia and direct human contact will be exploited in other ways, some fun and some professional. The latter can include distance learning and customer service applications, for example.

To transform our simple echo server and its client into an experimental chat system, we don't have to do much. The client, in fact, could stay the same, but it would be fairly confusing, since it has an extremely limited one-line display. We update it into a multiline scrolling record. In fact, we keep a record of both outgoing and incoming traffic. This is more than most chat designers would want, but it is useful as we experiment and debug.

The minimal upgrade of the server requires us to log in multiple users simultaneously and to share messages among them. A commercial chat system can add a lot of value by controlling the flow of chat messages to develop certain social structures. We create a simple any-to-all model.

The result we are trying to achieve is communication between clients. Though the server is essential-enabling technology, it is meant to be as transparent as possible. We want the clients simply to be aware of one another.

The architecture that best describes this communication is peer-to-peer connection. Serverless symmetry is the goal of many chat designers, game designers, and copyright-busting file-sharing systems.

While we want the feel of peering, we quickly find (as have many others) that in the practical world, it is best achieved through an invisible server. In our case, there are many obstacles. First the domain perimeter model keeps Flash from contacting any server other than at its home domain. With some inconvenience we could employ workarounds.

More intractable is the fact that Flash simply cannot provide the server side of a socket. The operating system, the user's connection provider, and the stability of the IP address all pose big problems further down the line. But we don't have to face these problems since Flash stops us first.

So we go forward with a server-centered system.


Client with History

The goal here is to extend the one-line input area and the one-line display of our Flash client so that we can view the past messages we sent and the ones that have come in. These displays will no longer be the same string. We will see messages from other clients, not just our own messages echoed back (Figure 20.1).

Figure 20.1. Text Fields for Display of Input and Output Histories

ActionScript
function transmit()      {
   textOut+="                                                ";
   textOut= "<msg>"+textOut.slice( 0, 36)+"</msg>";
   socko.send( new XML(textOut) );
   textOut3= textOut2;
   textOut2= textOut1;
   textOut1= textOut0;
   textOut0= textOut;
 textOut="";
 }
function received(msg)   {
 textIn3= textIn2;
 textIn2= textIn1;
 textIn1= textIn0;
 textIn0= textIn;
 textIn = msg.toString();
 }

We created four lines each of input and output history. Rather than any fancy, smooth, onscreen scrolling, we just scroll the data through the variables (Figure 20.2).

Figure 20.2. Input and Output History
    I l@ve RuBoard


Multiclient Server

The server-side changes are a bit deeper. Instead of the previous single-user solution, we need to create and track an entire list of client sockets. The way this works is not terribly different than what we did before, except that we do it repeatedly.

We need to establish a publicly known port to which we bind a listening socket. We listen for connection requests. Clients that request connections are each given an individual dedicated connection socket, and these are stacked in the server (Figure 20.3).

Figure 20.3. Six Steps to Connect Three Clients

PHP
<?php
header("Content-type: text/xml");

We are switching from an HTML monitor interface to an XML interface. This prepares us for a Flash control system, not a simple browser-based one. However as long as we use an XML-ready browser (like IE5.0 and beyond), it will also provide a fine browser interface.

The next two functions are unchanged from the single-serving version in the previous chapter. They are simple utilities.

PHP
function check( $value, $function )
{
   . . . . . etc
}
function superflush()
{
    . . . . . etc
}
function closedown()
{
global $port, $listener, $guests;
echo "<release> Freeing Port $port and this list:<br/>";
  echo "Listening Socket ".$listener."<br/>";
   for( $i=0; $i<count($guests); $i++) {
       echo "Client Socket ".$guests[$i]."<br/>";
       close( $guests[$i] );
       }
   echo "</release>";
   superflush();
}

The cleanup function has been extended. It originally shut down only two sockets, the listener socket and the message socket. Now it has a whole list of message sockets to process, as well as the listener socket. The list is an array called guests.

Notice that the output formatting has changed somewhat. The progress is now reported in XML, not HTML.

PHP
set_time_limit(120);
register_shutdown_function ("closedown" );
$address = gethostbyname ($SERVER_NAME);
$port =    12536;
$maxport = 12546;
$guests = array();
$welcome= "<WELCOME>Welcome to our little party</WELCOME>";
   check( $listener =   socket(AF_INET,SOCK_STREAM,0),  "socket" );
   while(  ($errno = bind( $listener, $address, $port)) <0 )
      if( ++$port > $maxport)
            die("<error>Tried up to $port".strerror( $errno )."</error>" );
      else
            echo("<try>".strerror($errno)."] Try $port<br/></try>\ n" );
  print( "<port>$port</port>" );
  superflush();

In this version, we are contending with a common development problem. As we debug small errors in our code, we need to run our software again and again. Often, when we try to reopen our assigned port soon after a small failure, we find it unavailable. The severity of this problem naturally depends on how serious the bugs are.

We are careful to put time limits on our scripts (see the first line). If we are patient, our system seems eventually to recover the locked-out ports. But the wait is often annoying.

The best solution is to make no errors. That's tough.

Our approach is to have the system administrator allocate a small range of port numbers for developmental use. The preceding code runs through this range seeking the first available port. It displays this successful port number so that we can then run our clients addressed to this port. The publication of this port number is in XML. We can read this number in an XML-ready browser and type it into the client. Of course a smart client would be able to read the XML itself, through an HTTP connection, and set its new port target automatically.

PHP
  check( $ret = listen( $listener, 5), "listen" );
  set_nonblock ( $listener );
  do {
      if( ($newguest= accept_connect( $listener )) >0 ){
         write( $newguest, $welcome, strlen($welcome));
         set_nonblock ($newguest );
         array_push( $guests, $newguest );
         }

The first step in the loop is to check for new users. Fortunately, nonblocking mode works well on listening sockets even in PHP 4.0.4, so if there are no pending connections to accept, we fall through the conditional immediately.

If a pending connection is found, the new guest is sent a welcome message. We set the new guest socket to be nonblocking as well and add it to our list of guests.

PHP
     $bufs=array();
     $buf="";
     for ($i=0; $i< count( $guests ); $i++)
        if(   read(  $guests[$i], $buf, 48) )
            array_push( $bufs, "<msg>"
                .strtoupper( strip_tags( $buf)  )."</msg>" );

The second step in our loop is to collect all the incoming text. The input line, $buf, and the array of such lines, $bufs, are both cleared. Each guest is checked to see if there is incoming data. If there is no data (and if your version of PHP—unlike 4.0.4—properly handles nonblocking reads), execution falls through the conditional. (Falling-through behavior is important, especially in this multiplexed server. We need to check all the guest connections for input. With only blocking reads, every client must contribute input to every cycle!)

Otherwise, the old tags are stripped off and the string is converted to uppercase (just for demonstration purposes). Fresh tags are applied, along with a proper null terminator. If more than one guest has something to say during this particular second, the lines are stacked up. In the case of a small number of guests with normal human typing abilities, this would be a rare occurrence. Ignoring the pending messages would not be critical; the overlooked messages would be picked up a second later. But our XMLSocket system works with data as easily as text, and the messages might be (for example) real-time game updates that are copiously generated and latency-sensitive. We would not want to backlog them.

PHP
     for ($i=0; $i< count( $guests ); $i++)
          for ($b=0;  $b< count($bufs); $b++)
             $writeerr = write($guests[$i], $bufs[$b], strlen($bufs[$b]));

The third step is to broadcast all the incoming messages back to all the users. In this version, each guest will see all the messages. The script goes through the list of guests and writes the same buffer to each socket.

It is easy to develop a more complex messaging system. For instance, a system that allowed private chat messages (individual or group) could filter messages easily by testing the guest's name against a destination attribute in each message.

PHP
     sleep(1);
     }   while (!stristr( $buf,"quit") );
  closedown();

With the sleep() function, we cede execution control of the processor. In this case, we let other processes use the CPU for 1 second between iterations of our loop. Without this sleep, our CPU usage could climb from 0.1% to 85% with no difference in performance. This test for quit is fairly crude since it only tests the most recent string, but it should suffice to get us started.

To put our Chat into action we need only install the swf file and PHP script in the same directory. Multiple users can open the swf file in their browsers and communicate. Typically these users are on separate computers, but for testing puposes there can be two windows on the same machine, as in Figure 20.4.

Figure 20.4. Two Chat Windows Communicating Via the Server
    I l@ve RuBoard


Beyond PHP

A programmer building a significantly more ambitious system than simple Chat might need more control and performance than PHP 4 offers. It would be worthwhile to consider programming environments that have more developed threading and networking features.

Other Languages

Java and C are obvious options, but there are open-source scripting choices as well. Python and, interestingly, Tcl offer approaches that might fit well with the applications requirements. This is especially true if your requirements include both a robust socket feature set and a simple scripting learning curve.

NOTE

TCL

TCL, like PHP, is a server-side command interpreter. Like PHP it is an open-source system that began as the work of one or two academics and now has a real commercial presence. Unlike PHP, Tcl has a fairly quirky syntax that owes little to C++ or any of the 'MLs.

It was invented pretty deliberately in the late 1980s by John Ousterhout (University of California Berkeley) as a generic scripting language called the Tool command language. The language became popular among Unix system administrators. Sun Microsystems hired Ousterhout and gave him a staff. They supported the language as an extremely early web development tool. Eventually the project spun out of Sun into its own small company. It is still headed by Dr. Ousterhout.

The sojourn at Sun was fortunate. Nowhere is networking software better understood, and it was there that Levy and Stanton added superb socket functionality to the language.

This suite of socket commands is available to create a Tcl-based server:

socket—server handler port

This creates a listener socket on the server at the specified port. When a connection request arrives from a remote client, and is successful, the callback handler is launched.

fconfigure channel options

Sets or queries many different I/O flow modes for a given socket (channel in Tcl jargon). Especially important for our purposes is control over blocking—which functions fine. There is also control over buffering and translation, and the read-only options sockname and peername which identify the process with whom you are communicating.

fileevent channel state handler

Registers an I/O event handler for a channel that is in nonblocking mode. When an event puts the channel into either the readable or writable state, the appropriate handler script will be called.

vwait variable

Puts Tcl in purely event-driven mode until the value of variable is changed (by one of the event handlers.) Unless this mechanism is used, the sockets cannot operate in non-blocking mode.

puts channel string

Write string to channel

gets channel variable

Read an incoming line from channel and store it in variable.

eof channel

Tests to see if input from channel has ended.

close channel

Close the socket.

Event-Driven Design

Glancing at the functions available to the Tcl programmer leaves us a bit wistful. Our polling PHP implementation is not so elegant: It stomps through the list of clients collecting messages and then again distributes them.

Even within PHP, there are other approaches that might be worth exploring. At the time of writing, PHP had undocumented interface to the Unix select() command, which allows the program to stand back and monitor the activity of a network of sockets. This is a useful tool when creating a high-performance multiplexed server for XML sockets.

Third Party Options

Commercial, open source and experimental XMLSocket Servers are available to brave developers. None is dominate—even the best is barely stable at this point, but the demand is clear and the market is responding. Among the commercial servers, FlashNexus—not functional at presstime—is pursuing an interesting direction. Rather than developing multiplexed XMLSocket Server, they are developing a bridge to the IRC network, where an enormous and extremely stable infrastructure exists which supports thousands of users simultaneously. A similar bridge has been built entirely in PHP by Sascha Schumann. Check online for the latest.


Conclusion

Multiplexed XML sockets allow us to scale the single-user live data socket from the previous chapter so that we can accommodate many simultaneous users addressing the same service at the same port address. This is almost invariably a real-world requirement.

XML sockets also enable communication among clients. Though this communication is mediated by the host computer, it can easily achieve the feel and function of peer networking.

The process we have sketched out is just a beginning. The technology and its application can both go much further.

We'll see you online.


Chapter 21. End Note

In completing the book, we can be proud.

As we started our last project, we were able to reach into a rather large toolchest. In it are XML skills and advanced Flash techniques that in earlier chapters we struggled to master. Now we casually bring them to bear in solving problems that are even more imposing.

Along the way we also developed a facility in web scripting and insight into a variety of networking protocols. We took a deep plunge into database programming and glimpsed some ways to approach the world that is larger than Flash—and larger than the web.

Mastery of technology means learning forever. Everything we have learned is useful now. But everything will require constant updating.

XML will probably be around for a long time. It might even avoid major distortion for a decade or so. But if it is an important foundation, it is only a foundation. It is a supporting technology for other protocols—WAP, RSS, SOAP, SMIL, SVG and many others. Learning these ever-emerging protocols and their competitors will keep you busy but enable you to build on the work and the ideas of others.

That is the honest promise of these technologies. By accommodating a common connective technology, we can leverage our work with the works of others in many interesting ways. Usually it is impossible to say who is exploiting whom.

It is our privilege to be alive at this happy moment. The world is wiring together. Technology allows us mix and share cultural distinctions that technology has not yet obliterated.

In previous generations, the wires of the world spread out from a few monopolistic centers. In the age of broadcasting, the wires pushed propaganda and brand messages. The wires have turned, and each of us is now at the center. Money matters less. Brains matter more. Imagination matters more. Humanity matters more.

You can do something interesting. Take these tools and use them well.

Thanks to those who have taught us, especially those who contribute help in the anonymous fraternity of the web.

Thanks to those who built the tools, and again especially those who freely share their time, their insight, and their sources: the builders of PHP, MySQL, etc., and XML.

Thanks to you for buying our book. We hope it was useful and pleasant. Please join us online to correct, extend, or question the material.


Appendix A. Microsoft Compatibility

    ASP Code with Flash

    IIS and PHP


ASP Code with Flash

ASP code permits a very simple web-based maintenance of our Quiz server. There is no database in this implementation. Instead a system of flat XML files is maintained.

ASP
<html>
<head>
<title>XML Upload Server</title>
</head>
<body>

Like PHP, an ASP file starts out in HTML mode, and an escape sequence such as <% marks the beginning of the interpretable script

ASP
  <%
    ' receives data (xml from flash)
   ' 1: back-up currently active quiz ( old/quiz.xml )
   ' 2: make newly editted quiz active ( quiz.xml )
   ' 3: archive today's latest version (eg: old/20011125.xml )
   ' 4: displays xml in a browser window
Option Explicit
Dim xmldom
Dim XMLstr

Option Explicit forces all variables to be declared before use. (It would be nice to have this option in ActionScript and in PHP. We have seen how frustrating it can be when every typo can silently generate a phantom variable and an elusive bug.)

ASP
' xmldom = Server.CreateObject("MSXML2.DOMDocument.3.0")
  xmldom = Server.CreateObject("MSXML.DOMDocument")

We first create a powerful XML object in the call to the MSXML dynamic library. This code runs better with Version 3 of MSXML. It also works better when we do not hardcode the version numbers into the object request. In particular this makes it easier to move the code between two very different installations in development and the publishing environments.

ASP
xmldom.load(Request)
XMLstr=xmldom.xml

We only need to ask the XML object to load itself from the data POSTed by the requesting client. Then we can create a string version of the object.

All this is much more sophisticated than PHP's simple event-driven parser. It is similar to the ActionScript implementation of the DOM-based functionality. (PHP ought to be there soon, however. At least two groups are now building new DOM-based XML modules for PHP.)

ASP
If IsEmpty( xmldom) Then
    Response.Write "XML load failure"
 '  Response.Flush
    If IsEmpty( Request.Form("xml")) Then
       Response.Status = 412
       Response.End

Send Precondition Failed error code to the client. It asked you to update the XML file but neglected to send the new data.

ActionScript
Else
      Dim fs
      Dim filename
      Dim backname
      Dim thisfile
      Response.ContentType="text/xml"
      Response.Write Request.Form("xml")

Request is the data object that arrived from the Client. Response is the data that the server is sending back in return. These lines tell the client to get ready for XML data and then echo back the XML we just received.

ActionScript
filename = server.mappath( "quiz.xml" )
backname = server.mappath( "old/quiz..xml")

We need the operating system's names for the files that the web server knows as "quiz.xml" and "old/quiz.xml". (These files do not have to actually exist. In particular the "old/" one might not yet have been created.) Rather than a relative address in a space limited to the Internet domain, the operating system uses a disk identifier and directory path on the server machine to reach the physical file.

ActionScript
Set fs = CreateObject("Scripting.FileSystemObject")

This fs object is not a file handle. It is a file system handle. It is the object that performs all the file-oriented operations we need.

Set thisfile= fs.GetFile( filename )
thisfile.Copy( backname )

We can address a file without opening it and instruct it to make a copy of itself as the backup file. If there is a backup file already, it is overwritten.

Set thisfile= fs.OpenTextFile(filename,2,True)
thisfile.write( Request.Form( "xml") )
thisfile.close

Now we open the file for writing and write into it the string representation of the XML object that was posted.

backname = server.mappath( "old/" & Year(Date()) &
        Month(Date()) & Day(Date()) & ".xml")
Set thisfile= fs.GetFile( filename )
thisfile.Copy( backname )

To create a permanent backup that will not be overwritten by the next update, we make an archive copy of today's new XML using today's date to name it.

Set thisfile = Nothing
Set fs = Nothing
End if

Then we recover the memory.

    Else
       Response.Write "Look I found it!"
       Response.Write XMLstr
End if
 %>
</body>
</html>


IIS and PHP

It is not necessary to learn ASP, even if you are compelled to work with a Microsoft server and operating system.

Internet Information Server will support PHP 4. Success has even been reported with the advanced socket manipulations required for XML sockets, using IIS server and Windows 2K, as long as the -enable-sockets directive has been compiled into PHP. (This is not the default.)


Appendix B. Scalable Vector Graphics (SVG)

Our work with XML uses the language to encode fairly abstract data like quiz questions or player identities. We use Flash to convert these into on-screen displays. Typography, layout and illustration—as well as animation and interaction—are employed to create a display that beautifies the data and hopefully clarifies its meaning.

There is a huge divide between the XML data and the graphics used to display it. The graph ics are transmitted as .swf files in Flash's proprietary encoding. The graphic data exist in a different address space not directly accessible to the functions that manipulate the XML data.

This divide may disappear. A very well-supported effort is underway to create an XML vocabulary for web graphics. The aim of this effort is universal browser support of a nonproprietary, highly sophisticated graphic language that meshes cleanly with the other technologies of the web.

Vector graphics, typography, and scripting are valued by this consortium. The standard that resulted from their efforts is called Scalable Vector Graphics (SVG). Its functionality overlaps the SWF functionality—it has a set of graphic primitives that are similar—but is somewhat more sophisticated.

The graphic primitives include the shapes of swf plus rectangles, circles, and more sophisticated bezier curves. The fill effects are like those of Flash, including outlines, gradients, masking, pattern fills, and transparency (all animatable), and also include plugin functionality similar to Photoshop filters, which can be applied to lines as well as fills.

SVG attaches font characteristics to true text: Rather than being buried in an swf file, pure text is exposed to search engines and indexing robots while retaining all the flexible styling ability that a Flash file would have.

All the tools of an interactive page can be integrated seamlessly into an SVG graphic: JavaScript, DHTML, XHTML, and CSS. The elements of a graphic can be distributed across many files or across many web sites, allowing great efficiencies of maintenance and flexibility of presentation to those who plan their site well.

Surprisingly, despite the fact that these graphics are presented in plain text XML, the early tests show files that are somewhat smaller than the matching SWF files (possibly because they were hand-encoded?). However, automatic zip compression will be introduced to the web at the same time as SVG. The text of SVG compresses easily, and the standard seems far more efficient than swf.

The future SVG-enabled browser (IE 6.0?) will be the equivalent of today's browsers with Flash player plugins. There are some important differences to look forward to. First, the SVG player will not be a proprietary system but will have support from multiple vendors. This is good and bad. Multiple vendors will compete for high-performance, highly compliant implementations, and that tends to result in better products brought rapidly to market.

On the other hand, veteran web developers will recognize the pain in store for them. In the past, we have had to straddle (for example) the slightly different renderings of HTML, Java, and scripting languages of Netscape, AOL, and Microsoft. The dream of a page rendering identically on different platforms will always be a dream. Maybe if we are lucky, it will display correctly on multiple platforms.

But the significance of SVG is greater than second-sourcing a player plugin. It is useful to have the graphics and script exposed in the transparent coding of XML. Transparent source code raises issues of site and transactional security. And it challenges (as most web protocols do) the competitive instinct to treat source code as a trade secret. Yet it opens up a new world of tremendously dynamic web entities that will make today's Flash sites seem stiff and unresponsive. And it invites the creation of a multiplicity of tools and libraries that push the envelope creatively, technically, and in fulfilling the needs of niche communities.

Even more significant than the transparency of the XML code is the integration of scripting, graphics, HTML, and XML into a single Document Object Model, at the core of the browser. An ActionScript programmer now cannot do something as simple as read an XML file containing (for example) sales data and create a simple area chart from it. While the XML data is accessible, the points in a polygon are not.

In an SVG environment, this code would be trivially simple. In fact, since the incoming sales information and the displayable SVG graph are both legitimate XML codings, the graphing function could exist in as simple a form as an XLS transformation file. Today a stock-charting site delivers dumb gif files. In the future it might combine XML-based data, a charting rubric (expressed in XLS and rendered in SVG) within a publisher's presentation coded in some kind of HTML. Each of these files is simple and independent. Each can be provided by a different supplier, allowing a web of flexible relationships and profound personalization.

When (and if) SVG will become ubiquitous is unknown, but momentum seems to be building. Macromedia has expressed support for the standard. IBM, Corel, and Adobe have been even more enthusiastic. Recent advances in Flash, starting with ECMA coding and XML readiness in version 5.0, can be seen as steps in the direction of this emergent standard. Hopefully the paths of Flash and SVG will continue to converge and the skills we are developing in ActionScript code and in XML will serve us well in the future.


Appendix C. Tools and Sources

A much better version of this appendix is online at flashandxml.com, along with code samples, bonus tracks, and bulletin boards. The online version features live links, free demos, and special programs for our readers.

Following are the tools we used during the writing of the book. This appendix is not meant to be an exhaustive view of the market or an analytical comparison of competitors. For example, we have low demands on development tools. Our work is not high-volume production but cautious experimentation. About 90 percent of the code was handwritten in very simple text editors (often web-based editors). The only tool that was truly indispensable (besides the Google search engine) was the Iris packet sniffer.


Network Tool

Iris

Iris is a packet sniffer: It monitors all the traffic going through a network connection. Iris can inspect every bit of traffic in a very lucid context. Without a tool like this, it is difficult to perfect a distributed network application. We are forced to guess what each application sends based on the response of the other applications. Very tough.

Directly examining packet dumps seems like tedious work, but it is precise and always illuminating. If you are building a system around connectivity, you ought to know what is really going on.

This tool is beautifully made and works flawlessly. Despite its considerable expense, it will certainly save time.

Thanks to Dave Rosenbaum who insists: "Don't assume. Test," and to Nigel Head who showed us how.


Servers

CULT XML Socket Server

The first commercial third party socket server. This product seems promising but was not ready for testing as we went to press.


XML Tools

XML Spy

This tool is engineered with enthusiasm and marketed with gusto. Fortunately, that marketing gusto has been toned down, and it no longer stamps its own name into every file you write! Like Real Networks, though, it still exhibits an unattractive desire to make itself the default app on dozens of file types.

Engineering enthusiasm makes for a finely built, highly featured product. But here too there is a sense of overkill. Maybe these guys need a harder job! Instead, they overinvented the user interface.

XML documents are straight tree structures. Trees are displayed with similar mechanisms in Mac and Windows Explorer and almost every app. Think of the directory structure you see in a file operation, with folders that open and contain other folders.

XML Spy visualizes trees as a clutter of nested boxes. This never made sense to us, although it might make sense to other people. We disregarded that very central aspect of the product, which promised a path to visual data design, and instead used XML Spy only as XML source editor, validator, and file manager. As such, it is excellent—well engineered and a pleasure to operate.

EditML

We spent less time with this XML editor. It is more modest than XML Spy, but, in our clumsy hands, it was also a bit less sturdy. It is designed as not only a tool but an introduction to the technology. Its help screens are excellent for teaching XML to the novice.

The product has an organization that is simple and easy to understand, although gratuitously cute icons give it an elementary school feel.

MS XML Notepad

This is a very simple and easy to use tool, but it is insufficient for designing valid XML. But the learning curve is a tiny bump, the price is zero, and the display of tree structure is free of the excessive creativity that makes the other two products annoying to use.

If you do not care about DTDs but you do care about money, this little gift from Bill Gates might be just fine.

Web Sources

www.w3.org/TR/REC-xml The definitive word on XML, literally.

editml.com/Samples.htm A nice set of simple starter sample XML files.

xmlpitstop.com A pleasant starting point with a fun attitude, a generous sysop, and hundreds of annotated links.

userland.com A highly active center for development of RSS, SOAP, and other interactive XML technologies. The focus is on pathways for independent servers to interoperate—such as automated content publishing and aggregation—and on functionality as content!


PHP Tools

PHP Coder

This is a very rich IDE (integrated development environment) for PHP. We used it a little bit, but we got our (beta) copy after we had become comfortable with simpler editors. Our projects were small, single-file experiments. If we were beginning a larger PHP project, this tool would probably be at the heart. In that case, the productivity payback for the considerable installation effort and the light learning curve would probably be pretty quick.

Sources

Zend.com The commercial arm of the PHP's hardworking mentors.

PHPBuilder.com An independent development oriented site with great community.


MySQL Tools

MySQLEditor

This is a mild pain reliever for assembling and interacting with a MySQL database. It makes communication a bit easier and helps visualization of the results. Version 0.3 (available to us) was still fairly rough.

Source

MySQL.com The source for everything including an excellent downloadable documentation.


I l@ve RuBoard Previous Section

Flash

Sources

macromedia.flash (newsgroup) An all-ages show, with some very patient flash masters…and much much noise.

flashkit.com A much more advanced group. Not for newbies. A rich web site with excellent discussions and heaps of resources. Also a conference in Sydney and Los Angeles, that addresses the accomplished Flash user.

figtree.com Top educators in advanced ActionScript (like Brendan Hall). The real thing.

moock.com Colin Moock approaches technology with creativity and generosity.

flashforward (conference) Held in both San Francisco and New York and drenched in information and attitude. Wear black, be pierced, and make sure it can all be expensed.
    I l@ve RuBoard Previous Section