Tải bản đầy đủ (.pdf) (81 trang)

MySQL /PHP Database Applications Second Edition phần 8 pps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (631.08 KB, 81 trang )

What’s all that other stuff? Well, the namespace definition states where the func-
tion getInseam() can be found on the network. The SOAP-ENV:encodingStyle
value further standardizes the way in which simple and complex data types are pre-
sented on each side of the SOAP transaction.
Next comes the question of whose inseam measurement you want to retrieve.
This specifier should be presented to the function as an argument, which is to say
that in a traditional (intra-program) call to the function the syntax looks something
like this:
GetInseam(“Joe Bloggs”)
In SOAP you’re obliged to do things a little differently. Remember that you are
already inside a getInseam element, which means you have already made clear
that getInseam is the function you’re calling. You need to specify the argument
now. Logically enough, you do that with an element whose name matches the argu-
ment name, as specified in the remote class:
<person xsi:type=”xsd:string”>Joe Bloggs</zipcode>
With that done, you close out the getInseam element and the Body element, as
well:
</ns1:getInseam>
</SOAP-ENV:Body>
How does all this look in practice? The next section takes a look at a request/
response pair in which a call to getInseam() is made and replied to.
A typical request/response pair
A SOAP transaction consists of a request and a response, similar in lots of ways to
the request and response that are made when you order up a Web page with your
browser. Remember, SOAP transmissions are nothing more than passages of text,
marked up with XML in such a way that they serve special SOAP purposes.
THE REQUEST
Here’s a complete request:
<?xml version=’1.0’ encoding=’UTF-8’?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV=” />xmlns:xsi=”


xmlns:xsd=” /><SOAP-ENV:Body>
<ns1:getInseam
522 Part IV: Not So Simple Applications
xmlns:ns1=”urn:referenceToWebService”
SOAP-
ENV:encodingStyle=” /><person xsi:type=”xsd:string”>Joe Bloggs</person>
</ns1:getInseam>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
A request, at its simplest, is just a Body element inside an Envelope element. You
can make things more complicated if you want— the specification allows for, among
other things, a supplementary Header element that describes the relationship
among several SOAP messages or that describes how the message should be routed.
THE RESPONSE
Responses, in terms of format, bear a close resemblance to requests. They have
exactly the same envelope formats, and the body is different only in terms of the
name given to the element being sent. Usually, that’s the same as the element name
specified in the request, with Response appended.
Here’s a complete response to match your earlier request:
<?xml version=’1.0’ encoding=’UTF-8’?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV=” />xmlns:xsi=” />xmlns:xsd=” /><SOAP-ENV:Body>
<ns1:getInseamResponse
xmlns:ns1=”urn:referenceToWebService”
SOAP-
ENV:encodingStyle=” /><return xsi:type=”xsd:float”>34.0</return>
</ns1:getInseamResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Not too complicated, right? Joe Bloggs has an inseam measurement of 34. That’s

probably 34 inches. However, could Joe be a child with a 34-centimeter inseam?
This response gives us no way to tell.
To encode complex data structures into SOAP messages (both requests and
responses), you have to dig a bit deeper into the specification. The next section
takes a look at how to encode an array into a SOAP message.
COMPLEX DATA TYPES
Complex data types are multipart data types. An array is an example of a complex
data type in which the members are accessed by number. A struct, as those of you
Chapter 16: SOAP 523
who code in C know, is an “associative array,” in which the array elements are
accessed by name rather than by number.
In the case of the inseam-returning Web service, it would be handy to know
what unit applies to the floating-point number that comes back in response to a
request. You can modify the contents of the Body element to hold this information
in a struct.
The struct defined here contains two elements: the value (the floating-point
value) and the unit (the string inches, centimeters, or whatever).
<return xmlns:ns1=”urn:referenceToWebService”
xsi:type=”ns1:inseamInfo”>
<unit xsi:type=”xsd:string”>inch</unit>
<value xsi:type=”xsd:double”>34.0</value>
</return>
In this mode of using SOAP, the Web service referred to defines a struct called
inseamInfo, which is comprised of a string called unit and a float called value.
By stating in the opening return tag that the return value is of type inseamInfo,
you make it legal to refer to these sub-elements.
There’s a lot more to do with the SOAP specification, and not all of it obscure.
Some of the more interesting and useful bits have to do with how errors and other
exceptional conditions are noted via SOAP, while others have to do with how to
describe other compound data types in SOAP messages. Such aspects of the specifi-

cation are beyond the scope of this chapter, but are certainly worth studying.
There’s lots of information on SOAP at the World Wide Web Consortium
site, including an overview ( />soap_intro.asp
) and a tutorial ( />default.asp).
Code Overview
Key to any successful career in software design is the ability to freely make use of
the work of other people. The open-source movement is all about this practice, and,
thankfully, a considerable amount of software is available for the taking. NuSphere
Corporation — makers of PHPEd, a PHP development environment — have developed
a set of classes called SOAPx4, which has since been modified and renamed
NuSOAP. It’s a remarkably capable SOAP suite, doing pretty much all the heavy lift-
ing for you. If you’re using a PHP development environment (such as NuSphere’s
PHPEd version 3 or later) you’ll probably find it even easier to work with NuSOAP.
You can add your own modules — such as new releases of PHP — to your environ-
ment after you set it up initially.
524 Part IV: Not So Simple Applications
The best place to begin the process of getting NuSOAP is on the Web site
of Dietrich Ayala ( His site
includes links to the latest version of NuSOAP, as well as links to documenta-
tion, mailing lists, and other resources for developers and architects.
The essence of NuSOAP
NuSOAP is a series of classes. You copy the downloaded files (most of them .php
files) to your include directory and then make reference to them in your own PHP
classes. The NuSOAP classes take care of such work as creating SOAP client and
server objects and managing the transmission of SOAP messages among those
objects. The NuSOAP classes even take care of something we discussed earlier in
this chapter: the encoding of values into properly structured XML.
For the most part, you can think of the NuSOAP classes as black boxes. You just
stick the PHP files in your include directory and then cease worrying about them.
All you have to do is be aware, as you’re writing PHP programs that you want to

act as Web-service providers or consumers, that you have some new classes avail-
able to you.
Suppose you want to build a server. In other words, you want to make a PHP
function available as a Web service. Once you’ve added the required include state-
ment (as follows) you have a four-step process ahead of you.
require_once(‘nusoap.php’);
1. Create a server object. All you need to do is set a variable equal to a
soap_server object (soap_server being one of the classes made avail-
able by your inclusion of NuSOAP). It’s easy:
$server = new soap_server;
2. Register one of your local functions with that new soap_server object.
Again, no problem. You simply invoke the register() function of the
soap_server object, specifying one of your local functions as the sole
argument. The complete syntax looks like this:
$server->register(‘getInseam’);
3. Define a function called getInseam(). This can contain whatever code
you want. Presumably, in this case, it accesses a database to retrieve a
named party’s inseam measurement and then returns a value and unit.
The skeleton of the function looks something like this:
function getInseam($name) {
// Function code
}
Chapter 16: SOAP 525
4. Tune the soap_server object in to the HTTP requests it’s meant to moni-
tor and enable it to respond to them. You do this with a standard piece of
code that rarely varies across NuSOAP applications:
$server->service($HTTP_RAW_POST_DATA);
Those are the key elements of a SOAP server as implemented under NuSOAP.
What, then, about the client that speaks to this service? It’s even simpler.
NuSOAP clients have to include the nusoap.php file as well. Once that’s done,

they need only to instantiate a soapclient object (the soapclient object, again,
being part of the NuSOAP collection) with the URL of the service to be called as an
argument. If you had a service called getInseam() on
(there isn’t one, by the way), you could do this to create a SOAP client to call it:
$soapclient = new soapclient(‘ />Then you could send a call to the server via that client, like this:
write( $soapclient->call(‘getInseam’,array(‘name’=>’Joe Bloggs’)));
Pretty cool, eh? The arguments are sent as an array that enables you to match
sent values to expected values as you like.
A simple NuSOAP service call
Now we take a quick look at a “Hello, user” program as written with the help of the
NuSOAP classes. Really you see two programs here: a client and a server. The server
exposes a simple routine that takes in a string (the user’s name) and returns a string
made up of the word Hello and the provided name followed by a period. In other
words, if you send the service Ralph as an argument, the service says, Hello,
Ralph.
First you need a server. The server has the same name as the service you want to
expose, so in this case name it hello.php. Its full contents are as follows:
require_once(‘nusoap.php’);
$server = new soap_server;
$server->register(‘hello’);
function hello ($name){
return “Hello $name.”;
}
$server->service($HTTP_RAW_POST_DATA);
526 Part IV: Not So Simple Applications
Not complicated, really. It’s just a matter of registering an otherwise ordinary
function with a special server object and then setting that server to deal with HTTP
activity.
Every server needs a client. The client file, in this case, can be called anything
and can reside anywhere the server can be accessed via HTTP.

require_once(‘nusoap.php’);
$soapclient = new soapclient(‘ />write($soapclient->call(‘hello’,array(‘name’=>’Ralph’)));
Pretty simple, really. You just bind your client to the service (this example
assumes you know exactly where it is and what it’s called) and call that client as
you need values from it.
The glory of NuSOAP is its simplicity. There’s more to it than we’ve just dis-
cussed — you will see some more complexity as the chapter continues — but there’s
no doubt that NuSOAP makes it unbelievably easy to incorporate SOAP client and
server capability into PHP programs. It can be said that, other than for educational
reasons, there’s never a reason to write your own SOAP client and server classes
anymore. You’d be reinventing an already highly refined wheel.
Determining the Goals
of the Application
It’s time to have a look at SOAP messaging under PHP, and at some of the ways you
can communicate with publicly accessible Web services via SOAP. The rest of this
chapter focuses on an application that requests information from different sources,
parses it, and presents it to the user.
Our goal is to use the Web services made available by a couple of providers — the
Barnes & Noble bookstore and the Babelfish translation service — to gather infor-
mation. Specifically, we use the Barnes & Noble service to gather information about
books that interest us, and the Babelfish service to translate a passage of text from
English into a series of European languages.
The Barnes & Noble application takes an International Standard Book Number
(ISBN) and returns the price of the book identified by that number at Barnes &
Noble’s Web site, www.bn.com. If you send it the value 0440234816, which is the
ISBN of Karen Marie Moning’s novel To Tame a Highland Warrior, you can expect
to see the following output from your local PHP program:
The price of book number 0440234816 is $6.99.
That price really is a bargain for “a medieval romance with paranormal overtones.”
Chapter 16: SOAP 527

The Babelfish application () enables you to
translate a phrase from English to French, German, Italian, Spanish, or Portuguese.
By default, the program is set up to send the value From beneath you, it devours
to Babelfish. The application gives you the following translations, which are vari-
ously accurate. Generally, if you see the English word in the translation, it means
Babelfish was stumped.

French — De sous vous, il devours.

German — Von unter Ihnen es devours.

Italian — Sotto da voi, esso devours.

Spanish — Debajo de usted, él devours.

Portuguese — Abaixo de você, ele devours.
Clearly, Babelfish has problems with devours.
The interesting aspect of this is that everything is done with Web services. You
send parameters (the ISBN in the former example, and the target language and
original phrase in the latter), and the Web services (which aren’t hosted locally, per-
haps obviously) return the strings you need.
How does it work? The answer to this question requires a deeper exploration of
our application’s code, which follows in the next section.
Code Breakdown
To see what’s going on in the two applications, you have to take a close look at the
PHP code itself. Both the Babelfish application and the Barnes & Noble application
are clients — they exist for the purpose of talking to servers that are implemented
somewhere else.
In this case, both of them speak to servers on XMethods (www.xmethods.com or
www.xmethods.net), a site that hosts a number of Web services for the purposes of

testing and experimentation. You don’t need to know how those remote services are
implemented. They could be in Java, C, or PHP. It really makes no difference to you,
because they’re set up to work with SOAP requests from over the Internet.
The Barnes & Noble application
The Barnes & Noble client sends an ISBN value (which uniquely identifies a book in
print) to a Web service, which returns the selling price of the corresponding book
on the Barnes & Noble Web site, www.bn.com. It prints out a simple string, indicat-
ing either the price of the book, the fact that www.bn.com doesn’t list the book, or
the fact that there was an error in the execution of the Web service.
528 Part IV: Not So Simple Applications
THE HEADER FILE
Many of the files in the Barnes & Noble and Babelfish applications call a header file
initially. The header file, header.php, does two important things. First, it imports the
critical nusoap.php file. It also specifies how the local applications deal with errors.
Here is header.php:
<?php
require_once(preg_replace(‘/soap.*/’,’book.php’,realpath(__FILE__)))
;
function soap_errors()
{
$oh = set_error_handler(‘error_handler’);
if (empty($oh))
{
set_handler(0, H_ERROR);
set_handler(E_ALL, H_LOG);
}
else
{
restore_error_handler();
}

}
soap_errors();
// include the class and function definitions for this application
require_once(‘lib/nusoap.php’);
?>
THE CLIENT FILE
The client file actually handles the process of instantiating a SOAP client that con-
nects to a publicly accessible Web service providing Barnes & Noble prices (really,
it just tells NuSOAP to do the dirty work). Here it is:
<?php
// include the SOAP classes
require_once(dirname(__FILE__).’/header.php’);
// define parameter array (ISBN number)
$param = array(‘isbn’=>’0385503954’);
// define path to server application
$serverpath
=’:80/soap/servlet/rpcrouter’;
//define method namespace
$namespace=”urn:xmethods-BNPriceCheck”;
Chapter 16: SOAP 529
// create client object
$client = new soapclient($serverpath);
// make the call
$price = $client->call(‘getPrice’,$param,$namespace);
// if a fault occurred, output error info
if (isset($fault)) {
print “Error: “. $fault;
}
else if ($price == -1) {
print “The book is not in the database.”;

} else {
// otherwise output the result
print “The price of book number “. $param[‘isbn’] .” is $”.
$price;
}
// kill object
unset($client);
?>
So, what’s going on here? Some of it should look familiar. First of all, the pro-
gram defines an array:
$param = array(‘isbn’=>’0385503954’);
Then, it sets a variable ($serverpath) that contains, as a string, a URL:
$serverpath
=’:80/soap/servlet/rpcrouter’;
What’s that URL? Well, if you call it up in an ordinary browser, you get an error.
The error says, in effect, that you can’t use a browser in this situation because this
isn’t a document at all — it’s a remote procedure call (RPC) router. As such, you have
to send it text (that is, SOAP XML) via the HTTP POST command. This makes sense,
because you want to send something from your client to the remote Web service.
Then you specify, on that remote site, the namespace you’re working with. This
line of code serves that purpose:
$namespace=”urn:xmethods-BNPriceCheck”;
The purpose of the reference to that site is to examine the Web Services
Description Language (WSDL) file that exists there. WSDL describes the Web services
that exist at a particular site, and the particular methods they expose. You know that
BNPriceCheck is a meaningful namespace on XMethods because you saw it adver-
tised at . (It’s also described programmatically at
/>530 Part IV: Not So Simple Applications
The next line should look familiar. It’s the instantiation of a NuSOAP soapclient
object that’s bound to the XMethods RPC router:

$client = new soapclient($serverpath);
With all that done, you can make a call to the remote service, expecting a single
value in return:
$price = $client->call(‘getPrice’,$param,$namespace);
That line invokes the call() function of the local soapclient object (as con-
tained in the $client handle). It sends along three arguments:

getPrice — The name of the function you are calling

$param — The struct you established earlier, containing the ISBN value

$namespace — The namespace you got from the WSDL file, making it clear
to the RPC router that you want to send your query to the Barnes & Noble
service
After the request goes off — and remember, it’s the job of NuSOAP to manage the
mechanics of sending the request over HTTP (using
POST) and dealing with the
response when it comes — you have only to react to the string that you get back.
It’ll be one of three things: An indication that the service experienced an error, an
indication that the ISBN you sent doesn’t correspond to a book in the database, or
a price value. Here’s the code that figures out which:
if (isset($fault)) {
print “Error: “. $fault;
}
else if ($price == -1) {
print “The book is not in the database.”;
} else {
print “The price of book number “. $param[isbn] .” is $”.
$price;
From that code you see that you’re expecting a floating-point value if the book

is in the database, or -1 if it’s not. If a variable called $fault (defined in NuSOAP)
exists, it means there was a problem in the service’s execution and an error field in
the Header element of the SOAP response was used (have a look at the SOAP spec-
ification for information on how Header elements indicate error conditions).
Chapter 16: SOAP 531
Be aware that floating-point values have some risk with respect to precision.
If you work with lots of numbers, rounding errors can occur, and when the
errors represent money,you can be in big trouble! Use the math functions to
guarantee precision if you have to, but don’t worry about it here — it’s not a
problem.
The Babelfish application
The Babelfish applications (there are three of them on the CD-ROM) are also
SOAP clients, similarly concerned with talking to a Web service made available on
the XMethods experimental site. The difference between these applications and
the Barnes & Noble application, though, is that the Babelfish applications rely
on the WSDL file exposed at XMethods (www.xmethods.com or www.xmethods.net)
to learn about the Web service.
For discussion purposes, consider babel.php, the simplest of the Babelfish SOAP
clients. The others vary mainly in that they do the same thing several times for dif-
ferent languages. Here is babel.php:
<?php
require_once(dirname(__FILE__).’/header.php’);
$client = new soapclient(
‘ />, ‘wsdl’
);
$proxy = $client->getProxy();
$languages = array(
‘English’ => ‘en’
, ‘French’ => ‘fr’
, ‘German’ => ‘de’

, ‘Italian’ => ‘it’
, ‘Spanish’ => ‘es’
, ‘Portugese’ => ‘pt’
);
$phrase = ‘From beneath you, it devours.’;
foreach ($languages as $language => $lc)
{
if ($language == ‘English’)
continue;
532 Part IV: Not So Simple Applications
$result = $proxy->BabelFish(‘en_’.$lc, $phrase);
$result = $proxy->BabelFish($lc.’_en’, $result);
$result = $proxy->BabelFish(‘en_’.$lc, $result);
$result = $proxy->BabelFish($lc.’_en’, $result);
print “English <-> $language : $result\n”;
}
?>
The most readily obvious difference between this and the Barnes and Noble
application is that the soapclient object (again, part of NuSOAP) uses a construc-
tor that’s different from the ones you saw earlier in this chapter. The soapclient
constructor used here makes reference to a WSDL file:
$client = new soapclient(
‘ />, ‘wsdl’
);
What’s that all about? Have a look at the WSDL file. It’s an XML file itself, so you
can call it up in your browser (or a text editor, if your browser won’t show XML
readably) if you like. The most interesting part of the file, for your purposes, is this
passage:
<operation name=”BabelFish”>
<input message=”tns:BabelFishRequest” />

<output message=”tns:BabelFishResponse” />
</operation>
That means there’s a function called BabelFish available to you. To call it you
need to create what NuSOAP calls a proxy object, which you do by calling a func-
tion of the soapclient object:
$proxy = $client->getProxy();
With that done, your local PHP program sets a variable containing the phrase to
be translated and an array containing abbreviations for the languages into which
translation is to be made. Each individual call to the Babelfish service goes through
the proxy object. The syntax for setting the variable looks like this:
$result = $proxy->BabelFish(‘en_’.$lc, $phrase);
The service is invoked, via the proxy object, with two arguments: the phrase to
be translated and the from/to language pair that describes the desired translation
procedure.
Chapter 16: SOAP 533
The seemingly redundant code in the foreach loop has a bit of fun with Babelfish,
highlighting the fact that a translation from Language A to Language B and back
again doesn’t necessarily yield the original phrase! This results, among other
things, from the gendered nature of many languages. Suppose you start with this
phrase in English:
If I had a hammer
Babelfish translates it into Spanish like this:
Si tenía un martillo
If you ask Babelfish to translate that Spanish back into English, though, you get
this:
If it had a hammer
This is because tenía can mean I had, you had, she had, he had, or it had (it’s
only in the subjunctive that Spanish is this vague). Only Babelfish’s programmers
know why the algorithm chooses it in this case.
Writing a SOAP server application

Sometimes, you just have to do things the hard way. Even though NuSOAP can
make the job a lot easier, the file simple.php does the work of a SOAP server man-
ually. It includes the code necessary to receive, parse, and evaluate SOAP requests,
and to send out correct SOAP responses. Up until now, you’ve worked with clients;
they requested data from publicly available Web services on the Internet.
Much of this file should look familiar to you. It receives a stream of raw text via
an HTTP POST event and uses a combination of string manipulation and PHP’s
built-in XML parsing capability to extract an argument (either CD or DVD). The pro-
gram kind of fakes a database lookup— you can put in actual code if you like — and
formats the “results” of the “lookup” as a SOAP response.
That’s the interesting part. The PHP program has to take on all the responsibility
of properly formatting the SOAP response. Essentially, the whole SOAP response
gets packed into a single variable, which is then returned:
$resp= <<<EOQ
<env:Envelope xmlns:env=” />xmlns:enc=” />env:encodingStyle=” />xmlns:xs=” />xmlns:xsi=” /><env:Header>
<t:Transaction xmlns:t=”urn:CDSpecial” env:mustUnderstand=”0”>
5
</t:Transaction>
</env:Header>
534 Part IV: Not So Simple Applications
<env:Body>
<a0:CDSpecial xmlns:a0=”urn:CDSpecial”>
<CDSpecialResponse>
<price xsi:type=”xs:string”>
$price
</price>
<title xsi:type=”xs:string”>
$title
</title>
<artist xsi:type=”xs:string”>

$artist
</artist>
</CDSpecialResponse>
</a0:CDSpecial>
</env:Body>
</env:Envelope>
EOQ;
This passage should look very much like the standard SOAP response you saw in
the theory sections earlier in this chapter. It’s all here: the namespace declarations,
the Envelope, Header, and Body elements, and the data-type designations describ-
ing the values that populate the Body.
Summary
This chapter covered a lot and in the process opened the door to a very exciting
new capability of PHP. Web services, as made possible by SOAP messaging, enable
you to extract information from the Internet without writing complicated text-
parsing routines or having to hope that the HTML your programs parse remains
formatted the same way forever. Web services are resources you can rely upon to
give you correct answers in response to correctly formatted function calls.
SOAP isn’t that hard to use, either, thanks in large part to the NuSOAP classes
from NuSphere and Dietrich Ayala. NuSOAP handles the tedious work of managing
HTTP requests and responses between clients and servers, and of formatting the
XML in SOAP messages as required. It makes Web services under PHP simpler by a
considerable margin.
Chapter 16: SOAP 535

Chapter 17
Project Management
IN THIS CHAPTER

Introducing project management software


Discussing the problems a project management application must solve

Examining the database tables for a project management application
THE PROBLEM IS ALMOST NEVER the code. When you’re developing a large application—
or any project really — the hairiest difficulties have more to do with people working
in teams than with any technical aspect of the job.
The program in this chapter aims to facilitate the process of collaboration
between people working on a project. It will keep track of deadlines and keep notes
on which person is responsible for which tasks in a project.
Determining the Goals
of the Application
For the purposes of this application, project management has to do with the division
of labor between two or more collaborators. We want them to be able to see who’s
responsible for what, to know when deadlines are (or were), and to view the contents
of files. As an ancillary function, this application also requires authentication, so the
software knows which user is which and can adjust its output accordingly.
Necessary pages
The catalog of pages our project management program requires closely resembles
the list of requirements it must satisfy:

The project management application must support user logins, and it must
keep track of users as they use the application across many transactions.
This makes session management a necessity. We use PHP’s library of
session-management functions and objects, just as we did in other appli-
cations in this book.
537

Once users have logged in, they should be able to view the status of the
projects they’re involved in. They’ll want to see which files they are

responsible for (own, in the parlance), when they are or were due to be
finished, what any revised expected completion dates are, and whether
any problems have been noted with regard to individual files.

Users should be able to share files that are relevant to a project.
The pages are straightforward — as you’ve guessed by now, we certainly didn’t
blow the budget on a designer. Figures 17-1 through 17-5 show you some of the
main screens of the application.
Figure 17-1: Project home page
538 Part IV: Not So Simple Applications
Figure 17-2: Edit Project page
Figure 17-3: Calendar view
Chapter 17: Project Management 539
Figure 17-4: Add New Task page
Figure 17-5: Project Admin home page
540 Part IV: Not So Simple Applications
Designing the Database
Key to this application is its database. Much of the work that the project-management
application does is essentially note keeping about which files fit into which projects,
where they are, who owns them, and what their status relative to established dead-
lines is. This information is the sort that databases like to contain for us.
The entirety of the project management information store is contained in a data-
base called projects. The projects database contains a number of tables, each of
which tracks various aspects of the project management mission. Figure 17-6 shows
the complete schema:
Figure 17-6: The projects database
User types
The values that can be assigned to users to dictate their privileges are contained in
the user_types table. The table contains an incremented integer field called
user_type_id that serves as the primary key, and a user_type field that contains

English words that describe the type of user.
users
user_id
user
password
user_type_id
email
firstname
lastname
company
phone
files
file_id
project_id
task_id
description
public
owner_id
upload_dt
first_upload_dt
fullpath
mimetype
file
tasks
task_id
project_id
task
description
due_dt
status_id

public
owner_id
first_due_dt
completion_comments
projects
project_id
project
project user map
project_id
user_id
status
status_id
status
admin
username
password
user types
user_type_id
user_type
Chapter 17: Project Management 541
The SQL scripts that create the database and populate its tables initially put the
values “staff,” “client,” and “admin” into the user_type fields of three records.
Application users
The users table defines the people who are allowed to use the database, as well as
their usernames, passwords, and a bunch of personal contact information. The
fields in this table are as follows:

user_id — An auto-incrementing integer used as the primary key

user — The username


password — The password

user_type_id — An integer that matches the primary key of one of the
user type constants in the user_types table

email, firstname, lastname, company, and phone — Strings representing
personal details
Project and task status
The status table contains another set of values, describing whether a project or
task is late, on schedule, on a delayed schedule, completed, or abandoned.
The descriptions of these values are “On Track,” “Slipped,” “Overdue,” “Done,”
and “Cancelled.”
Projects
In this application, a project is defined as an entity in its own right. Though pro-
jects typically contain many files, it is the files that are mapped to projects, not the
reverse. Projects are recorded in the projects table.
Project records contain seven columns:

project_id — An auto-incrementing integer used as the primary key

project — The project name

description — A text description

due_dt — The current due date of the project (possibly not the same as its
original due date)

status_id—The status of the project, defined by a reference to the status table


owner_id — The manager of the project, defined by a reference to the
users table

first_due_dt — The original due date of the project; by comparing this
field to due_dt, you can see whether or not the project has been delayed
542 Part IV: Not So Simple Applications
Project-user mappings
The project_user_map table exists solely for the purpose of establishing a correla-
tion between a project record, as identified by its project_id integer and stored in
the project_id field, and a user, identified by its user_id integer and stored in the
user_id field.
Tasks
Tasks are like files in that they are associated with project, and many of them can be
associated with a single project. Tasks are recorded in a table called tasks, which
contains 10 fields:

task_id — An auto-incremented integer that serves as the table’s
primary key

project_id — An integer that maps to a project_id value in the project
table

task — A varchar name for the job

description — A text description of the task to be done

due_dt — A date variable describing the originally scheduled completion
date

status_id — An integer that maps to the status_id field in the status

table

public — A Boolean value describing whether users other than the owner
can see the task

owner_id — An integer that maps to the user_id field of the users table to
identify the task’s owner

first_due_dt — A date object denoting the current scheduled completion
date for the task

completion_comments — A text field for notes that are made upon
completion
Files
Projects contain many files, and files map to projects. The project management
application keeps track of projects’ constituent files in a table called, logically
enough, files. The files table contains 11 fields that describe the files:

file_id — An auto-incrementing integer used as the primary key

project_id — An integer that maps to a value in the project_id field in
the projects table
Chapter 17: Project Management 543

task_id — An integer that maps to the task_id field in the tasks table

description — A text description of the file (not necessarily including its
path)

public — A Boolean value that describes whether users other than the

owner can access the file

owner_id — An integer that maps to the user_id field of the users table to
identify the file’s owner

upload_dt — A date object describing when the file was last uploaded

first_upload_dt — A date object describing when the file was first
uploaded

fullpath — The path and file name of the file in the file system, relative
to the project root, recorded as text

mimetype — A varchar description of the file’s MIME characteristics

file — The contents of the file itself
Code Overview
Aside from the database, the core of the project management application’s func-
tionality is in a series of PHP pages that closely parallel the application’s essential
functions (refer back to the requirements described earlier in this chapter in the
“Necessary pages” section). The program also makes use of a series of classes that
serve as software representations of logical entities, such as database connections.
Further, it uses a library of PHP snippets to guarantee that HTML pages throughout
the application have a consistent look, and that the pages’ design can be updated
easily.
Logging in and establishing a session
Users enter the application via the login page, login.php, which goes through the
process of presenting the user with an HTML form that collects a username and
password. The application validates these against the database before forwarding the
user to index.php, the main interface page. The login page also serves the critical

function of establishing a session with the user, enabling the application to distin-
guish him or her from other users across many request/response transactions.
Showing active projects
The main interface page, generated by index.php, provides the user with his or her
main project management “dashboard.” It shows the user, at a glance, what projects
he or she owns, what the status of each is, and how each one is performing relative
544 Part IV: Not So Simple Applications
to its deadline. The user can see what tasks are associated with each project, and
can establish a new project record in the database.
Creating a new project
If the user chooses to create a new project, he or she is prompted for all the details
the database requires in its project record. The user gets an HTML form that asks for
the project’s title and description, its due date, its present status, and its owner
(which may be someone other than the user who’s adding the project).
Uploading a file
The application allows the user to associate files with projects. The application vali-
dates the path the user specifies and stores the file the user uploads — which the user
does via a file upload interface of the sort that’s standard to his or her operating
system — in the files table of the database.
Viewing a file
When a version of a project file exists in the files table of the database, the applica-
tion displays it to the user. The application lists the PHP code directly into the user’s
browser, where he or she can examine or save it.
Adding a user
Administrator-class users have the ability to add and edit users. They can determine
usernames and passwords and adjust the personal information kept about each user.
Code Breakdown
How does the application do all this? The application is really quite modular and
incorporates a lot of functions and other pieces you’ll probably find useful in other
kinds of applications. The details of the code are worth a look.

Session management
The first order of business on the login page is to figure out whether the user who
has accessed the page is already in a PHP-managed session. If so, it may not be
necessary for the user to log in manually at all. The application may be able to ver-
ify his or her credentials automatically and just present the requested page.
The code for doing this appears right at the top of the login page (and in several
other locations throughout the application files, as well). It looks like this:
Chapter 17: Project Management 545
check_session();
if (!isset($_SESSION))
global $_SESSION;
That code looks for a variable called $_SESSION. If the variable isn’t around, it
creates a new instance of it, effectively putting the user into a new session for
tracking purposes.
The function check_session() is interesting. It’s one of a library of functions
written expressly for this book. It makes sure the user’s environment (a browser,
typically) is set to support persistent cookies, and if so, it starts a session:
function &check_session()
{
if (!isset($_SESSION))
{
trace(‘_SESSION is not set’);
if (php_sapi_name() == ‘cli’)
{
global $_SESSION;
$_SESSION = array();
trace(‘can not use cookies, in CLI - set _SESSION to
array()’);
}
else

{
trace(‘ok to use cookies, call session_start()’);
session_start();
}
}
trace(‘results of check_session: _SESSION=’, $_SESSION);
return $_SESSION;
}
If the browser does not support cookies, or the user has them turned off, then the
code reacts politely, and other operations continue without session tracking.
Authentication
Now you can have a look at the way the application handles user authentication.
The file login.php handles the work of prompting the user for a username and pass-
word, using an HTML form. What’s interesting is that this form makes use of the
variables $_SERVER[‘PHP_AUTH_USER’] and $_SERVER[‘PHP_AUTH_PW’], which
are PHP values that represent the username and password entered by the user.
546 Part IV: Not So Simple Applications

×