+ All Categories
Home > Documents > Developer Shed Network PHP

Developer Shed Network PHP

Date post: 08-Dec-2016
Category:
Upload: buidung
View: 225 times
Download: 5 times
Share this document with a friend

If you can't read please download the document

Transcript

[Developer Shed Network] Server Side - PHP - Using PHP with LDAP (Part 1).pdf Using PHP With LDAP (part 1) By Harish Kamath 2003-04-04 Printed from DevShed.com URL: http://www.devshed.com/Server_Side/PHP/PHPwithLDAP1/ Plugging In One of the reasons for PHP's immense popularity is its support for a wide variety of different Internet technologies. It is this support, coupled with the speed with which new language extensions get added to the source tree, that keeps PHP ahead of other competing languages in the same category, and ensures the continuity (and future growth) of the fan following it currently enjoys. One such Internet technology is LDAP, the Lightweight Directory Access Protocol. In case you're not familiar with LDAP, it is a protocol designed to allow quick, efficient searches of directory services. Built around Internet technologies, LDAP makes it possible to easily update and query directory services over standard TCP/IP connections, and includes a host of powerful features, including security, access control, data replication and support for Unicode. PHP has shipped with support for LDAP since version 3.x of the language, and today comes with built-in support to connect to LDAP databases, perform queries and process result sets. These capabilities make PHP extremely popular among developers who need to create Web applications that interface with LDAP directories - for example, online address books and Web-based directories and directory search agents. Though these sound like intimidating projects to take on, they're actually pretty simple - as you'll see, PHP makes otherwise complex processes seem almost friendly via its powerful functions and sophisticated capabilities. Over the next few pages, I'm going to introduce you to the LDAP functions that ship with PHP, demonstrating how they can be used to rapidly create a Web-based interface to an LDAP directory; this interface will allow users to perform queries and add and remove entries from the directory using a standard Web browser. I'll assume here that you have a working knowledge of PHP fundamentals, a development environment with a working PHP build, and a sense of humour. LDAP knowledge would be a bonus, although it is not a requirement - this tutorial does include a brief discussion of LDAP basics. Got 'em all? Flip the page, and let's get going! Looking For Answers In case you're not familiar with LDAP, this section is designed to give you a crash course in the basics. It is not intended to be exhaustive - you should take a look at the links at the end of this article for more detailed material - but it should get you through the remainder of this article without confusing you too much. An LDAP directory is usually structured hierarchically, as a tree of nodes (the LDAP directory tree is sometimes referred to as the Directory Information Tree, or DIT). Each node represents a record, or "entry", in the LDAP database. An LDAP entry consists of numerous attribute-value pairs, and is uniquely identified by what is known as a "distinguished name" or "DN". If you're familiar with RDBMS, it's pretty easy to draw

an analogy here: an LDAP entry is analogous to a record, its attributes are the fields of that record, and a DN is a primary key that uniquely identifies each record. Consider the following example of an LDAP entry, which might help make things clearer:

dn: [email protected], dc=my-domain, dc=com objectclass: inetOrgPerson cn: Sue sn: Jones mail: [email protected] telephoneNumber: 1 234 567 8912

This is an entry for a single person, Sue Jones. As you can see, the different components of the entry - name, email address, telephone number - are split into attribute-value pairs, with the entire record identified by a unique DN (the first line of the entry). Some of these attributes are required and some are optional, depending on the object class being used for the entry (more on this later); however, the entire set of data constitutes a single entry, or node, on the LDAP directory tree. Since LDAP entries are arranged in a hierarchical tree, and since each node on the tree can be uniquely identified by a DN, the LDAP model lends itself well to sophisticated queries and powerful search filters. For example, I could restrict my search to a particular subset of the tree simply by specifying a different base for the query to begin from, or query only against specific attributes in the directory tree. Heck, I could even do both, and feel like a Real Programmer! The Bare Necessities In order to get started with PHP and LDAP, there are a couple of things you need. First, you need a LDAP server, which you can use as the foundation for your PHP scripts. While there are many commercial LDAP servers, the one used in this article is the OpenLDAP server, which is robust, scalable and - best of all - free. You can download a copy from http://www.openldap.org(this tutorial uses OpenLDAP 2.1.16). Once you've got your LDAP server up and running, it's time to populate it with some entries. Here are the sample entries that I will be using throughout this tutorial.

dn: dc=my-domain, dc=com objectClass: dcObject objectClass: organization o: MyOrganization dc: my-domain.com dn: [email protected], dc=my-domain, dc=com objectClass: inetOrgPerson cn: Keith sn: Richards mail: [email protected] dn: [email protected], dc=my-domain, dc=com objectClass: inetOrgPerson cn: Joe sn: Somebody mail: [email protected] dn: [email protected], dc=my-domain, dc=com objectClass: inetOrgPerson

cn: Sarah sn: Nobody mail: [email protected] telephoneNumber: 23 67 128 5639

Refer to the OpenLDAP reference manual, or the usage guide that shipped with your LDAP server, for the exact commands needed to import this data into your LDAP directory. Note that the server must be set up to allow read access without a password, and its base DN should be "dc=my-domain, dc=com", as in the sample data above. Once the LDAP server has been set up, the next step is to configure PHP to communicate with this server. PHP 4.3 supports this via its LDAP extension, which is not activated by default - you may need to recompile your PHP binary with the "--with-ldap" parameter to activate LDAP support (Windows users get a pre-built binary with their distribution). Once you've got PHP configured with LDAP support, check to see that all is well via a quick call to phpinfo() - you should see something like this:

Code Poet Now that all the requirements are in place, let's put together a simple PHP script to connect to the LDAP server and display the contents of the directory. Take a look at the following code.

Run the script in your browser, and you should see something like this:

Let's take a closer look at this script. Anatomy 101 At the onset, I should tell you that communicating with an LDAP server through PHP is very similar to communicating with a database server - open a connection, use the connection to search for some information, iterate over the resource handle containing the results of the search and close the connection. 1. Let's begin with the basic connection.

The ldap_connect() function is used to open a connection with the LDAP server. If a connection is possible, the function returns a link identifier that is used for all subsequent communication with the LDAP server. If a connection is not possible, the function returns false. By default, the ldap_connect() function looks for an LDAP server on port 389. In case your LDAP server is running on a non-standard port, you can specify that port number as an additional argument to ldap_connect(), as in the following code snippet.

2. Once a connection is established, the next step is to "bind" it to the LDAP server via the ldap_bind() function call.

In the example above, this is a so-called "anonymous" bind, as no authentication credentials are provided in my call to ldap_bind(). 3. Now for the meat of the script - the ldap_search() function.

This function requires three parameters - the LDAP link identifier, the DN of the location in the LDAP hierarchy where the search should begin, and the search query string itself. In the example above, I'm asking the system to begin searching at the root of the hierarchy, and return all entries which have a valid common name ("cn") attribute. The return value of ldap_search() is a result set with the entries that match the query. 4. Once a result set has been obtained, the entries within it may be placed in a multi-dimensional array via a call to ldap_get_entries().

Every element of this array corresponds to one entry from the LDAP directory. Each of these elements (entries) is further structured as an array, whose elements correspond to the attributes of each entry; these attributes may be accessed either by name or index number. A "for" loop is used to iterate over this multi-dimensional array, and return a subset of the data within it. A count of all the entries in the result set can be obtained via a call to ldap_count_entries()

5. Once the result set has been processed, the connection to the LDAP server can be closed via a call to ldap_close().

It's generally a good idea to close the connection once you're done using it, so that system resources used by the script are freed up and made available to other programs. The five steps outlined above make up a general template for interacting with an LDAP server through PHP. You will see that these steps (and the code that they consist of) recur over almost every example in this tutorial. What's In A Name? The example on the previous page was fairly static - all it did was return a list of all the entries in the directory that had a "cn" attribute. This next example makes things a little more interactive - it includes a form where the user can enter a name and search the LDAP server for that name. Here's the form,

Search

and here's what it looks like:

And here's the code for the search script:

The only difference between this script and the previous one is that this time, the search criteria is dynamically generated using the data POSTed from the form.

$query = "(cn=" . $_POST['name'] . ")";

This parameter is then passed to the ldap_search() function and processed in the standard manner. Here's what you should see, assuming that you searched for the string "joe":

Simple when you know how, isn't it? And that's about it for this first part. In the second part of this article, I will be showing you how to carry out more complex searches, and also add and delete information from the LDAP directory. Stay tuned for that one.and, until next time, stay healthy! Note: Examples are illustrative only, and are not meant for a production environment. Melonfire provides no warranties or support for the source code described in this article. YMMV!

This article copyright Melonfire 2000-2002. All rights reserved.

[Developer Shed Network] Server Side - PHP - Using PHP with LDAP (Part 2).pdfBy Harish KamathThis article copyright Melonfire 2000-2002. All rights reserved.

Table of Contents................... 1Looking Back................. 2Of Needles And Haystacks.................... 4Making Lists................... 6Adding It All Up................. 8Changing Things Around.................. 9Wiping Out The Past.................. 10To Err Is Human........................ 11Endgame

Printed from DevShed.com

i

Looking BackIn the first part of this article, I gave you a crash course in the basics of using PHP with LDAP,demonstrating how PHPs built-in support for LDAP makes it easy to query an LDAP directory server foruser information. That article also included a detailed discussion of the steps that make up aPHP-to-LDAP session, together with examples demonstrating how those steps play out in the real world.In this second (and concluding) article, I will carry the torch onwards, explaining how to perform morecomplex searches with LDAP. Since PHP comes with a whole bunch of functions for updating LDAPdirectory servers, Ill also examine those, with examples of how they can be used to add, modify anddelete entries to the LDAP directory tree. Lets get going!

Printed from DevShed.com

1

Of Needles And HaystacksIn the first part of this article, I had written a simple PHP script to search the LDAP directory for aparticular user. But real life is never that simple... Lets suppose I want to search for my friend Joe from high school. Now, there are a million Joes in theworld, and I would like to drill down to my Joe without having to navigate through a large set of results.The solution? A more complex search, which uses additional parameters to restrict the result set.Take a look at the new search form,

Search First name

Last name

Email address

which looks like this:

Once the form above is submitted, the data entered by the user is sent to the search script "search.php",which actually performs the query - take a look:

The structure of the code is identical to that of the examples in previous pages - with one importantdifference:

This search string, obviously with the variables replaced with actual values, is passed to the ldap_search()function; it returns only those entries from the LDAP directory free which match *all* the parametersspecified. Why? Because of my usage of the special AND operator, signified by the addition of theampersand (&) to the beginning of the search string above.Heres what the output looks like:

Printed from DevShed.com

2

If your LDAP entries contain other attributes, its just as easy to create more complex search queries -simply add more input fields to the search form, and update the search string above to use those attributeswhen searching the directory tree.In case you were wondering, yes, you can also use logical OR (the | operator) or logical NOT (the !operator) in your search queries - Ill leave that to you to play with.

Printed from DevShed.com

3

Making ListsSo that takes care of searching. Now, how about adding, editing and deleting entries?PHP comes with a full-fledged API that allows easy modification of the LDAP directory tree. In order todemonstrate how this API works, Im going to build, over the next few pages, a simple administrationmodule that performs these functions, so that you can see how its done.First up, we need an index page that lists all the entries in the directory. This index page will serve as thestarting point for an administrator to make changes to existing directory entries or add new ones. Heresthe code,

First Name Last Name Add new entry

and heres what it looks like:

As you can see, most of this code is similar to what you saw in the previous article. However, there is oneimportant difference - instead of using the ldap_search() function, Im using the ldap_list() function,which returns a one-level list of all the entries matching the specified criteria, given a base DN at which tostart searching.

This base DN and search filter are provided to ldap_list() as second and third arguments respectively. Inthe example above, the ldap_list() function returns all the entries which have a "cn" attribute and arelocated immediately under the node with DN "dc=my-domain,dc=com".Additionally, ldap_list() accepts a fourth, optional parameter - an array containing a list of all the attributesthat should be included in the result set. In the example above, this array is called $params, and it specifiesthat the returned result set should contain the "cn", "sn" and "mail" attributes.The search result identifier returned by the ldap_list() can be passed to the ldap_get_entries() function,which does the dirty work of extracting the raw data into a structured array. This array can be processedusing a simple "for" loop.

Printed from DevShed.com

4

Note also the links to "edit.php" and "delete.php" next to each entry - Ill be discussing the scripts theselinks point to shortly. For the moment, though, skip downwards to the last link on the page, which pointsto "add.html" - this is the HTML form that is used to add new users to the database, and its discussed onthe next page.

Printed from DevShed.com

5

Adding It All UpNow that you know how to pull data out from the LDAP directory, how about putting some in?PHP has a function to do this as well - Ill show you how to use it, by creating a simple interface to addinstances of the "inetOrgPerson" class to the LDAP directory.First, the input form, "add.html":

Add Entry First name Last name E-mail address

Youll notice here that Ive only used three attributes of the "inetOrgPerson" class - "cn" for the commonname, "sn" for the surname and "mail" for the email address. Feel free to add to this list if you like.Heres what the form looks like,

and heres the script that actually adds the entry:

Before I get into the details, lets give this code a quick test run. Enter some data into the form above andsubmit it - you will probably see something like this:

Ugly, huh?In order to add an entry to the LDAP server, you must provide the server with appropriate credentials -something Ive obviously not done in the example above. Typically, these credentials consist of thesuperusers DN and password - information that you should have set when setting up your LDAP server.Assuming you have this information, lets modify the code above and give it to the LDAP server.

Printed from DevShed.com

6

Note the addition of user credentials in the call to ldap_bind() - these credentials will be used toauthenticate the PHP client and allow it to make changes to the LDAP directory.

Note also that LDAP requires you to provide the complete DN of the superuser, not just the username (asis common with other authentication mechanisms).Once thats taken care of, the next step is to create an associative array whose keys correspond toattributes of an LDAP entry. The data for these attributes is obtained from the HTML form submitted bythe user.

Once thats done, I also need to construct the DN for the new entry. In this case, Ive used the emailaddress as a component of the entrys DN in order to ensure uniqueness (LDAP DNs at the same level inthe hierarchy must be unique).

In case youre wondering where all this is going, you should know that all this information is needed bythe ldap_add() functions, which is the PHP function that actually takes care of adding a new entry to theLDAP directory. This functions requires three arguments: a link identifier for the LDAP connection, theDN for the new entry, and the actual attributes of the entry. Since I now have all this in place, all thatremains is to call ldap_add() and save the data to the LDAP server.

And heres what the result looks like:

In case youre wondering about the numerous calls to ldap_error() in the code above, ignore them for themoment - Ill be explaining them in detail shortly.

Printed from DevShed.com

7

Changing Things AroundNext up, modifying entries. You might remember, from previous pages, that the index page of thisapplication included links to scripts named "edit.php" and "delete.php" next to each entry, and passed eachof those scripts certain data using the URL GET method. Heres what that code looked like:

Edit

As you can see, the users email address is passed from the main index page to the "edit.php" script on theURL when the user clicks the corresponding hyperlink. This email address can then be used, incombination with the ldap_list() function, to retrieve the complete user record and display it in a form forediting - which is exactly what the next script does:

First Name false[estimatedTotalResultsCount] => 3880

Using The Google Web APIs With PHP

Plugging In 10

[directoryCategories] =>

[searchTime] => 0.05159[resultElements] => Array([0] => Array([cachedSize] => 6k[hostName] =>[snippet] => ... Check out. what we'vecooked up in our brandnew PHP section. Copyright19982002
Melonfire. All rights reserved Terms and Conditions |Feedback.[directoryCategory] => Array([specialEncoding] =>[fullViewableName] =>Top/Computers/Internet/Web_Design_and_Development/Designers/M)

[relatedInformationPresent] => true[directoryTitle] => Melonfire[summary] => A production house with businessdivisions focusing on content production and Web development.[URL] => http://www.melonfire.com/[title] => melonfire!)

[1] => Array([cachedSize] => 11k[hostName] =>[snippet] => ... Copyright 19982002Melonfire. All rights reserved
Terms and Conditions|Feedback.[directoryCategory] => Array([specialEncoding] =>[fullViewableName] =>)

[relatedInformationPresent] => true[directoryTitle] =>[summary] =>[URL] =>http://www.melonfire.com/community/columns/trog/archives.php?category=PHP

Using The Google Web APIs With PHP

Plugging In 11

[title] => The Melonfire Community Trog)

// remaining array elements snipped out //

)

[endIndex] => 10[searchTips] =>[searchComments] =>[startIndex] => 1[estimateIsExact] => false[searchQuery] => melonfire)

Obviously, this is not very useful but we're just getting started. Flip the page, and I'll show you how tomassage all this raw data into something resembling a search results page.

Using The Google Web APIs With PHP

Plugging In 12

Chasing LibertyIf you take a close look at the output of the previous example, you'll see that the call to doGoogleSearch()results in a PHP associative array containing a series of result elements, together with some statistics on thesearch itself. It's extremely simple to use this array to create an HTML page containing a properlyformattedlist of matches to the query term. Here's an example:

Error

Search ResultsYour search for produced hits.

Most of this is identical to what you saw in the previous example, except that, this time, instead of justdumping the result array to the screen, I've used a "foreach" loop to iterate through it and display the matchesas items in a bulleted list. Note how the various keys of the SOAP response array can be used to build the listof matching Web pages, with descriptions and URLs.

In the event that the procedure generates an error on the server, the response array will contain a SOAP fault.It's generally considered good programming practice to check for this and handle it appropriately you'll seethat I've done this in the script above.

Here's what the end result looks like:

Using The Google Web APIs With PHP

Chasing Liberty 14

Using The Google Web APIs With PHP

Chasing Liberty 15

The Sum Of All PartsThus far, the two examples you've seen have had the search term hardwired into them. Needless to say, thisisn't very useful in the real world it's far more sensible to have the search term generated dynamically viauser input. The next example does just that, demonstrating how you can add a fullfledged, Googlebackedsearch engine to your application.

Search (s) near

Nomatches! Go backback

This article copyright GBdirect Ltd 2000. All rights reserved. Reprinted from http://www.gbdirect.co.uk/ with permission.

WAPEnabling a Website with PHP3

Switching to WML 6

http://www.gbdirect.co.uk/http://www.gbdirect.co.uk/

ConclusionsIt's dead easy if you follow the path of separating the data management from the presentation. Our approachworks fine for us and we can easily add features to the WAP part of the site to bring it up to a similar spec tothe main HTML site. Whether we would want to is another matter. WML is pretty ropey and it's my ownopinion that people used to HTML browsers will be extremely frustrated if they get suckered by the hypeabout surfing the net from your mobile phone. Screens the size of typical mobile phones are wellnighunusable and devices with more real estate will have enough power to run HTML browsers. The datacompression of the WAP protocol is another thing couple that with HTML and you will have somethingmuch more interesting.

The drawbacks that I've seen from using php3 like this are minor. The htmlspecialchars() function inphp3 doesn't deal correctly with the WML entities that need escaping, but I intend to write my own versionand put it in the stdwaphdr.tpl include file. If php3 finds a syntax error it tries to be nice and tell the uservia the browser but it outputs HTML not WML while it does it. Hardly a showstopper if you have got yourphp3 code right in the first place, but should it happen then the user's browser will choke. I'm told that WAPphones are even less reliable than Windows at the moment: an article in this month's Personal ComputerWorld describes having to remove the battery from the phone when it gets wedged.

It works, it's simple and I'm sure we will be doing more with this.

Mike Banahan, GBdirect, 22nd April 2000

Update 22nd May 2000If you want a copy of the templates used on the wap site, I have prepared a gzipped tarfile of them which youcan use for information. Note that the server will serve them up as text/plain just click on `save as' and dropthe file into an appropriate directory before unpacking it. The files will unpack into the current directory, somake sure you put the archive somewhere sensible first. If you spot any errors, I'd love to know.

In response to a number of comments I've received to this piece, I have written a description of experiencesdeveloping WAP/WML pages as a followup item.

This article copyright GBdirect Ltd 2000. All rights reserved. Reprinted from http://www.gbdirect.co.uk/ with permission.

Conclusions 7

http://somewherenear.com/wap/templates.tgzhttp://www.gbdirect.co.uk/http://www.gbdirect.co.uk/

Table of ContentsIntroductionHow it WorksSwitching to WMLConclusions

[Developer Shed Network] Server Side - PHP - Watching the Web.pdfBy The Disenchanted Developer

This article copyright Melonfire 20002002. All rights reserved.

http://www.melonfire.com/

Table of ContentsCherchez La Femme...........................................................................................................................................1

A Little Research.................................................................................................................................................2

Code Poet.............................................................................................................................................................4

Digging Deep........................................................................................................................................................7

Backtracking.....................................................................................................................................................10

Plan B.................................................................................................................................................................11

Closing Time......................................................................................................................................................15

Watching The Web

i

Cherchez La FemmeSo there I was, minding my own business, working on a piece of code I had to deliver that evening, when thepretty darkhaired girl who sits in the cubicle behind me popped her head over and asked for my help.

"Look", she said, "I need your help with something. Can you write me a little piece of code that keeps track ofWeb site URLs and tells me when they change?"

"Huh?", was my first reaction...

"It's like this", she explained, "As part of a content update contract, I'm in charge of tracking changes to aboutthirty different Web sites for a customer, and sending out a bulletin with those changes. Every day, I spend themorning visiting each site and checking to see if it's changed. It's very tedious, and it really screws up my day.Do you think you can write something to automate it for me?"

Now, she's a pretty girl...and the problem intrigued me. So I agreed.

Cherchez La Femme 1

A Little ResearchThe problem, of course, appeared when I actually started work on her request. I had a vague idea how thismight work: all I had to do, I reasoned, was write a little script that woke up each morning, scanned her list ofURLs, downloaded the contents of each, compared those contents with the versions downloaded previously,and sent out an email alert if there was a change.

Seemed simple but how hard would it be to implement? I didn't really like the thought of downloading andsaving different versions of each page on a daily basis, or of creating a comparison algorithm to test Webpages against each other.

I thought there ought to be an easier way. Maybe the Web server had a way of telling me if a Web page hadbeen modified recently and all I had to do was read that data and use it in a script. Accordingly, my first stepwas to hit the W3C Web site, download a copy of the HTTP protocol specification, fromftp://ftp.isi.edu/innotes/rfc2616.txt, and print it out for a little bedside reading. Here's what I found, halfwaythrough:

The LastModified entityheader field indicates the date andtime atwhich the origin server believes the variant was lastmodified.

There we go, I thought the guys who came up with the protocol obviously anticipated this requirement andbuilt it into the protocol headers. Now to see if it worked...

The next day at work, I fired up my trusty telnet client and tried to connect to our intranet Web server andrequest a page. Here's the session dump:

$ telnet darkstar 80Trying 192.168.0.10...Connected to darkstar.melonfire.com.Escape character is '^]'.HEAD / HTTP/1.0

HTTP/1.1 200 OKDate: Fri, 18 Oct 2002 08:47:57 GMTServer: Apache/1.3.26 (Unix) PHP/4.2.2LastModified: Wed, 09 Oct 2002 11:27:23 GMTAcceptRanges: bytesContentLength: 1446Connection: closeContentType: text/html

Connection closed by foreign host.

A Little Research 2

As you can see, the Web server returned a "LastModified" header indicating the date of last change of therequested file. So far so good.

Watching The Web

A Little Research 3

Code PoetWith the theory out of the way, I was just about ready to make my first stab at the code. Since I was told thatthere are a large number of URLs to be monitored, I decided to use a MySQL database table to store them, inaddition to a brief description of each URL. The table I came up with is pretty simple here's what it lookedlike:

CREATE TABLE urls (id tinyint(3) unsigned NOT NULL auto_increment,url text NOT NULL,dsc varchar(255) NOT NULL default '',date datetime default NULL,email varchar(255) NOT NULL default '',PRIMARY KEY (id));

And here's a sample of the data within it:

mysql> select * from urls;+++++++++++| id | url | dsc | date | email|+++++++++++| 1 | http://www.melonfire.com/ | Melonfire.com | NULL |[email protected] || 2 | http://www.yahoo.com/ | Yahoo.com | NULL |[email protected] || 3 | http://www.devshed.com/ | Devshed.com | NULL |[email protected] |+++++++++++3 rows in set (0.00 sec)

Next up, I needed a script that would iterate through this database table, connect to each of the URLs listedwithin it, and obtain the value of the "LastModified" header basically, replicate what I did with my telnetclient, as many times as there were URLs. Here's what I put together:

Code Poet 4

0){// open message to be forwarded and parse it to findattachment$inbox = @imap_open ("{". $SESSION_MAIL_HOST . "/pop3:110}",$SESSION_USER_NAME, $SESSION_USER_PASS) or header("Location:error.php?ec=3");$structure = imap_fetchstructure($inbox, $id);$sections = parse($structure);

// go through attachment list and message section// if a match exists, create a MIME section and import thatattachmentinto the message// do this as many times as there are attachments to beincludedfor ($x=0; $x

You can now return to the message list,or compose another message.

Building A PHPBased Mail Client (part 3)

Setting Boundaries 23

Yes, this is pretty complicated but fear not, all will be explained.

1. The first order of business is to perform certain basic checks on the data that is being submitted to"send.php". Consequently, in addition to the routine session check, I've added some code to ensure that themessage has at least one recipient.

2. If an attachment has been uploaded, it's also a good idea to verify that the upload was successful. In order toperform these checks, I'm using the four variables created by PHP whenever a file is uploaded $attachmentholds the temporary file name assigned by PHP to the uploaded file, $attachment_size is the size of theuploaded file, $attachment _type holds the MIME type, and $attachment_name holds the original name of thefile (you can read about this in detail at http://download.php.net/manual/en/features.fileupload.php)

3. Next, I need to perform some basic validation on the addresses entered into the form. I already have afunction do you remember validate_email()? to perform this validation, but I need to first clean up theaddresses entered into the form and reduce them to the [email protected] form.

The following code performs the address validation, redirecting the browser to the error handler if any of theaddresses turn out to be invalid.

In case you're wondering, the clean_address() function is a tiny little function to reduce an email address tothe [email protected] form, removing (among other things) whitespace, angle brackets and real names from theaddress text.

Building A PHPBased Mail Client (part 3)

Setting Boundaries 25

4. The next step is to actually create headers reflecting the recipient information.

Assuming that no attachments exist, this is a great place to stop; all that's left to do is send the message usingPHP's mail() function. But the attachment handling code is where all the meat really is and it's explained onthe next page.

Building A PHPBased Mail Client (part 3)

Setting Boundaries 26

Under ConstructionIn the event that attachments do exist, a couple of additional steps are required:

5. First, a unique boundary marker needs to be generated in order to separate the distinct parts of the messagefrom each other. This boundary needs to be added to the message headers so that MIMEcompliant mailclients know where to begin and end extraction of message parts.

Note that the term "attachment" here refers to two types of attachments: an uploaded attachment, or (only inthe case of forwarded message), an attachment included from the original message. My message constructioncode must account for both types of attachments.

6. Next, a MIME message needs to be constructed, with the message body sent as the first section and theencoded attachments following it. Note the additional "" that has to be appended to the specified boundarywithin the message itself omit it and your MIME message will not be parsed correctly by aMIMEcompliant mail client (you may remember this from the second part of this article).

At this point, $str holds the message body.

7. Assuming that this is a forwarded message which includes attachments, I'll need to first retrieve the selectedattachment(s) from the original message. This involves connecting to the mail server, retrieving the messagestructure, parsing it with my custom parse() function, and pulling out the textencoded attachment.

Each included attachment is then added to the message string ($str) that is being constructed, with appropriateheaders to describe the attachment type and name.

8. Next, we need some code to handle uploaded attachments (the second type). In this case, the uploaded fileis in binary format and needs to be first encoded into BASE64 format before being attached to the message.

The binary attachment is first converted into a textbased BASE64 representation with PHP'sbase64_encode() function, and then added to the message string ($str). Note the chunk_split() function, usedto split the BASE64encoded text into data chunks suitable for insertion into an email message, and theHTTP upload variables created by PHP, used to add information to the ContentType: andContentDisposition: headers.

9. With all attachments handled, the final task is to end the MIME message with the boundary marker and anadditional "" at the end, signifying the end of the message.

10. Finally, mail() the message out and display a status message indicating whether or not the mail wasaccepted for delivery. Note that the Bcc: header is not correctly processed on Windows systems.

Whew! That was complicated, but I think the effort was worth it. This script can now accept data entered intoany of the three forms described previously, in addition to possessing the intelligence necessary to encodeuploaded attachments or import forwarded ones. And it works like a charm try it and see for yourself!

Building A PHPBased Mail Client (part 3)

Under Construction 31

When Things Go Wrong...The last script an extremely simple one is the error handler, "error.php". If you look at the source code,you'll notice many links to this script, each one passing it a cryptic error code via the $ec variable. Verysimply, "error.php" intercepts the variable and converts it to a humanreadable error message, which is thendisplayed to the user.

Here's what it looks like.

Building A PHPBased Mail Client (part 3)

When Things Go Wrong... 33

Simple and elegant not to mention flexible. Found a new error? No problem assign it an error code and let"error.php" know.

Building A PHPBased Mail Client (part 3)

When Things Go Wrong... 34

Game OverAnd that's about it for this case study. We've covered a whole range of different things over the past couple ofweeks session management, HTTP headers and file upload, code modularization, MIME attachments, mailserver connection and message retrieval, and a whole lot more and I hope you found the process interestingand entertaining.

This case study, though slightly longer than usual, also demonstrates that application development for theWeb requires a great deal more than just a knowledge of PHP. In order to develop an efficient, scalable anderrorfree Web application, developers must have a fundamental understanding of the protocols and designprinciples of the Internet, and must be able to apply this knowledge judiciously, always selecting thetechnology and approach that is optimal for a particular situation.

This is no easy task it can take years to develop this approach but the payoff, both in terms ofbetterdesigned code and an overall feeling of satisfaction, is well worth it. Putting together this mail clientwas, at times, a frustrating exercise in trial and error, but I've come out the other end with a greaterunderstanding of how email works, and a greater respect for the people whose job involves designing andimplementing email solutions.

I found the following resources invaluable during the development process if you're interested in learningmore about the various topics discussed in this case, you should make it a point to visit them:

The PHP manual page on POP3 functions, at http://download.php.net/manual/en/ref.imap.php

The PHP manual page on session management functions, athttp://download.php.net/manual/en/ref.session.php

The PHP manual page on HTTP upload, at http://download.php.net/manual/en/features.fileupload.php

The PHP manual page on string functions, at http://download.php.net/manual/en/ref.strings.php

The PHP manual page on mail functions, at http://download.php.net/manual/en/ref.mail.php

The official POP3 RFC, at http://www.faqs.org/rfcs/rfc1939.html

The official SMTP RFC, at http://www.faqs.org/rfcs/rfc2821.html

The official MIME RFC, at http://www.faqs.org/rfcs/rfc2045.html

It should be noted at this point that this project is by no means complete. Since this prototype was introducedfor internal use a few days back, a number of minor bugs have been reported, and some additional capabilitiesrequested. Consequently, the source code provided in this article should be treated as prerelease code,unsuitable for use in production environments; I still have a ways to go before releasing this as a product toour customer.

Among the features requested: the ability to upload multiple attachments at a time; to supportHTMLencoded messages; to allow sorting of the message listing by name, size, date and owner; to allow forgroup replies; to limit the number of files displayed per page; to change the default colours; and to display the

Game Over 35

http://download.php.net/manual/en/ref.imap.phphttp://download.php.net/manual/en/ref.session.phphttp://download.php.net/manual/en/features.file-upload.phphttp://download.php.net/manual/en/ref.strings.phphttp://download.php.net/manual/en/ref.mail.phphttp://www.faqs.org/rfcs/rfc1939.htmlhttp://www.faqs.org/rfcs/rfc2821.htmlhttp://www.faqs.org/rfcs/rfc2045.html

name of the currently loggedin user. Unusual MIME attachments also tend to result in unpredictablebehaviour again, this is something that I need to debug and tune further.

I plan to continue adding features and optimizing code as and when time permits if you'd like to give me ahand, or have ideas on how to improve the techniques discussed here, drop me a line and let me know. Ciao!

Note: All examples in this article have been tested on Linux/ig86 with Apache 1.3.12 and PHP 4.0.6.Examples are illustrative only, and are not meant for a production environment. Melonfire provides nowarranties or support for the source code described in this article. YMMV!

Building A PHPBased Mail Client (part 3)

Game Over 36

Table of ContentsThe Road AheadComposing YourselfReturn To SenderComing ForwardSetting BoundariesUnder ConstructionWhen Things Go Wrong...Game Over

[Developer Shed Network] Server Side - PHP - Building a Quick-and-Dirty Guestbook with patGuestbook (Part 1).pdfBy Harish Kamath

This article copyright Melonfire 20002002. All rights reserved.

http://www.melonfire.com/

Table of ContentsSo Much Noise,So Little Time............................................................................................................................1

Introductions.......................................................................................................................................................2

Home Sweet Home..............................................................................................................................................3

The Voice Of The People....................................................................................................................................6

Playing The Field................................................................................................................................................7

Code Poet.............................................................................................................................................................8

User, User, On The Wall..................................................................................................................................10

Building A QuickAndDirty Guestbook With patGuestbook (part 1)

i

So Much Noise,So Little TimeOne of the nice things about the Web has always been the diverse mix of people on it. In the relativeanonymity of the Web, artists and sculptors hang out sidebyside with lawyers, accountants and CEOs, eachwith their own particular brand of views and opinions (which they're usually only too happy to express, toanyone who cares to listen!)

This multilayered mix of opinions and feedback makes today's Web the modern equivalent of the Tower ofBabel. It's also one of the most important reasons for its popularity very few other places have the samelevel of energy, the same sense of refreshment, and the same feeling of excitement every time you visit.

Given this highlyinteractive nature of the beast, it's no surprise that Web sites are also gradually becomingmore and more accepting (and responsive) to user feedback. And, for a very long time, the simplest and mostefficient way of capturing this user feedback has been via a socalled electronic "guestbook", which allowedvisitors to a Web site to record impressions about their visit and provide suggestions for improvement.

A guestbook is also one of the best ways to transform a Web site from a oneway channel of communication from publisher to reader into a more interactive twoway street, where users can provide comments on thecontent or features of the site, and the site managers can respond with opinions, corrections or enhancements.This immediately has benefits for both parties involved a better user experience for the site's visitors, andfeedback from the horse's mouth for the site managers.

Now, adding a guestbook to your site is pretty easy. Most often, your Web hosting provider will offer you aguestbook as part of your hosting package. In case you don't get it bundled in your contract, you can buy onefrom the numerous service providers out there, or even sit down and program one yourself.

If you picked the last option (either by necessity or simply because you're bored), then you're going to be gladyou found this article. Over the next few pages, I'm going to be discussing how to integrate a free,opensource PHP class named patGuestbook into your Web site, so as to rapidly, efficiently andcosteffectively add guestbook capabilities to your Web presence.

The upside? You'll finally know what your visitors really think. The downside? The truth might hurt...

Flip the page if you're ready.

So Much Noise,So Little T... 1

IntroductionspatGuestbook is a PHPbased application described, in the author's words, as "a versatile guestbook with awealth of configuration options". Developed by Stephan Schmidt, it is freely available for download and ispackaged as a single PHP class which can be easily included in your application.

Some of patGuestbook's more interesting features include quick installation and configuration, easyintegration into any Web site, multiple guestbooks, easy administration of guestbook entries, automatic emailnotification of new guestbook entries...the list goes on. If you're the fiddly type who likes to play withadvanced settings, the program comes with lots of those, too ratings, customization of the interface usingskins, moderator mode for guestbook entries, and support for socalled patExtras.

Written as a PHP class, patGuestbook can easily be integrated into any PHPbased Web site, and cansubstantially reduce the amount of time you spend coding guestbooks. You'll find it functional, powerful and(if you're the kind who likes fiddling) easy to customize, and it'll soon be a standard component of every Website you develop.

Before proceeding further, you should visit the patGuestbook home page at http://www.phptools.de/ anddownload a copy of the latest version. The package contains the main class file, documentation outlining howto use it, and some example scripts.

Introductions 2

http://www.php-tools.de/

Home Sweet HomeSince it's a PHP script, patGuestbook needs to live somewhere under your PHPaware Web server's documentroot typically, somewhere in the directory structure holding the rest of your Web site. For purposes of thistutorial, I shall assume that the application is located in the "patGuestbook" directory under the Web serverroot, and is accessible at the URL http://localhost/patGuestbook/

When you uncompress the source archive, you should see a directory structure that looks something like this:

patGuestbook||| admin| config| skins| include| sql

Once you've got the files in an appropriate location, you also need to create some database tables to storepatGuestbook data (if you're already using a database for your site, simply add patGuestbook's tables to thatdatabase). Look in the source archive's "sql" directory, where you'll find an SQL script to create two tables "patgbguestbooks", which holds the master list of all the active guestbooks, and "patgbentries", which storesthe actual guestbook entries.

Here's a quick glance at the SQL commands to create these tables:

DROP TABLE IF EXISTS patGbEntries;CREATE TABLE patGbEntries (id int(10) unsigned NOT NULL auto_increment,guestbook int(10) unsigned NOT NULL default '0',date datetime NOT NULL default '00000000 00:00:00',name varchar(100) default NULL,email varchar(200) default NULL,homepage varchar(200) default NULL,entry text NOT NULL,comment text,ratings text,flags set('approved') default NULL,PRIMARY KEY (id),KEY guestbook (guestbook),KEY flags (flags)) TYPE=MyISAM;

#

Home Sweet Home 3

DROP TABLE IF EXISTS patGbGuestbooks;CREATE TABLE patGbGuestbooks (id int(10) unsigned NOT NULL auto_increment,name varchar(20) NOT NULL default '',template varchar(255) NOT NULL default '',owner varchar(255) default NULL,entriespp tinyint(3) unsigned NOT NULL default '0',flags set('moderated','disabled','notify') NOT NULL default'',fieldconf text,PRIMARY KEY (id),UNIQUE KEY name (name)) TYPE=MyISAM;

Next up, configuration. patGuestbook configuration is handled via a single file named "patGuestbook.php",located in the source distribution's "config" directory. It's fairly selfexplanatory, take a look:

Most of this is pretty standard information patGuestbook needs to know the database access parameters sothat it can get to its tables, and the location of the directories containing screen templates for the guestbookadministration module and the guestbook itself (these templates are used by the patTemplate engine to rendereach page of the application, and can be customized to your needs more on this later).

If you're using the default installation, you shouldn't need to change any of the information in this file, exceptthe database access parameters. If, on the other hand, you're not using the default installation paths and areplan to integrate patGuestbook into your existing site structure, you should also alter the directory locations inthe configuration file to reflect where the templates are located.

Building A QuickAndDirty Guestbook With patGuestbook (part 1)

Home Sweet Home 4

You can now check to see if patGuestbook is up and running, by visiting the URL (remember to alter this asper the installation path on your system) http://localhost/patGuestbook/example.php

patGuestbook should load up and display the following error message:

Could not identify guest book!

No need to panic this is just an indication that the application could not find any guestbook in the system.This is probably because you have not yet created one but hey, at least it confirms that you've got myconfiguration right.

Next, how about wiping out that error message by actually creating a guestbook?

Building A QuickAndDirty Guestbook With patGuestbook (part 1)

Home Sweet Home 5

http://localhost/patGuestbook/example.php

The Voice Of The PeoplepatGuestbook come with a comprehensive administration module to manage the application, accessible at theURL http://localhost/patGuestbook/admin/guestbook.php (remember to alter this as per the installation pathon your system). Here's what it looks like:

Again, a very simple interface. Use the "New Guestbook" command near the top of the page to create a newguestbook and define some basic parameters for it.

First, you need to specify a name for the guestbook say, "Voice Of The People". Next, you need to specifyan email address to which entries should be delivered (if required), the number of entries to be shown to theuser on each page, and the name of the template to be used for entry display. You also have someadministrative parameters to deal with email notification of new entries, moderation of guestbook entriesand whether the guestbook should be active or inactive.

Use the "Save Changes" button to save your changes, and you're done...or are you?

The Voice Of The People 6

http://localhost/patGuestbook/admin/guestbook.php

Playing The FieldOnce you've create the guestbook, you need to configure the fields within it via the "Configure Fields"command. By default, all guestbooks can capture the following information from visitors: name, emailaddress, URL, and comments.

A configuration module allows you to control these fields, allowing you to decide, for example, which fieldsshould be displayed to the user, which fields are required and which are optional, and the labels to displaynext to each field.

For the moment, you can leave the options in their default state, or change them if you like. When you'redone, use the "Save Changes" command to save your changes.

patGuestbook also offers you the ability to allow visitors to answer one or more multiple choice questions, afeature it calls "ratings". Use the "Configure Ratings" command to view the ratings that are currentlyconfigured. Since this is a new guestbook, there are none at the moment; however, you can use the "Add anew rating possibility" command to add the question and possible answers to the system.

In case you haven't figured it out yet, the rating label is the question that should be displayed to the user, withthe list of available options specified, one on each line, in the large text box following it. You can also specifywhether answering the question is mandatory or optional. When you're done, use the "Save Changes"command to save your changes.

Playing The Field 7

Code PoetNow that the guestbook has been configured, all that's left is to integrate it into your Web site. And even that'sa pretty simple task to accomplish pop open the included sample PHP script, "example.php", which lives inyour source distribution's root directory, and take a quick peek at what's inside it (I've added comments to thecode so that it's easier to see what's happening):

Nothing too complicated here. First, the script include()s the files that contain the API functions required bypatGuestbook.

// include required classesrequire_once( "config/patGuestbook.php" );

Code Poet 8

require_once( "include/patGuestbook.php" );require_once( "include/patTemplate.php" );require_once( "include/patDbc.php" );

This is followed by initialization and creation of the patGuestbook object, which serves as the focal point forall future guestbook operations, together with the template engine and database connection point.

// initialize template engine$template = new patTemplate;

// initialize DB abstraction layer$dbc = new patMySqlDbc( $db_host, $db_name, $db_user,$db_pass );

// initialize guestbook$guestbook = new patGuestbook;

// connect guestbook to template engine and DB connection$guestbook>setTemplate( $template ); $guestbook>setDbc( $dbc);

Once that's done, the patGuestbook object's process() method can be used to select and display the guestbook.

// display guestbook$guestbook>process( array( "name" => "Voice Of The People" ));

You can use the barebones skeleton above to create your own PHP scripts, or even integrate the lines of codeabove into your existing site. Either way, this is all you need to do to get a guestbook up and running.Painless, isn't it?

Building A QuickAndDirty Guestbook With patGuestbook (part 1)

Code Poet 9

User, User, On The Wall...So that takes care of the geeky stuff that goes into the backend of the system. Now, how about actuallychecking out what a user to your site would see when they launched the guestbook module?

In order to view the user experience for yourself, point your browser to the example script above, athttp://localhost/patGuestbook/example.php, and you should see something like this:

You can now use the "Add New Entry" command to enter a comment in the guestbook.

As you can see, the fields displayed correspond exactly to what you specified when configuring the guestbook(just to see if it all works as it should, try leaving out a required field when submitting your comment patGuestbook should barf and display a whole string of errors). When you're done, use the "Save Entry"button to save your comment, and the application will take you back to the main guestbook page, whichshould now display the comment you just entered.

User, User, On The Wall..... 10

http://localhost/patGuestbook/example.php

You can now link to this script from other pages in your site, and start watching as the comments roll in!

That's about it for the first part of this article. Of course, this isn't all you can do with patGuestbook in thesecond part of this article, I'll show you how you can customize the look and feel of the default guestbook,together with instructions on how to moderate and deactivate individual entries. Make sure you come back forthat!

Note: All examples have been tested on Linux/i586 with Apache 1.3.28, PHP 4.2 and patGuestbook 1.0.Examples are illustrative only, and are not meant for a production environment. Melonfire provides nowarranties or support for the source code described in this article. YMMV!

Building A QuickAndDirty Guestbook With patGuestbook (part 1)

User, User, On The Wall..... 11

Table of ContentsSo Much Noise,So Little TimeIntroductionsHome Sweet HomeThe Voice Of The PeoplePlaying The FieldCode PoetUser, User, On The Wall...

[Developer Shed Network] Server Side - PHP - Building a Quick-and-Dirty Guestbook with patGuestbook (Part 2).pdfBy Harish KamathThis article copyright Melonfire 2000-2002. All rights reserved.

Table of Contents.................. 1Bells And Whistles............... 2Adopting A Moderate Approach.................. 3If Looks Could Kill.................... 5Bringing In The Database.................. 7A Well-Formed Plan................. 9When Things Go Wrong................... 10Locking It Down................... 12Over And Out

Printed from DevShed.com

i

Bells And WhistlesIn the first part of this article, I gave you a quick rundown on the patGuestbook application, right fromdownloading the application to the nitty-gritty of installation, configuration and basic usage. This wasfollowed by an express introduction to guestbook creation and deployment. Now, in this concludingarticle, I shall focus on some of the bells and whistles offered by the application to the more enthusiasticdevelopers out there (count me in as a permanent member of this group). Among the items underdiscussion: controlling the entries listed in the guestbook, customizing the user interface with the includedpatTemplate class, and protecting access to the applications administration module. Keep reading!

Printed from DevShed.com

1

Adopting A Moderate ApproachIf you recollect, one of the options available at guestbook creation time was related to guestbook entrymoderation. In the first part of this article, I had decided to leave this at its default setting and notmoderate the entries for my guestbook; as a result, entries were immediately displayed on the site as soonas they were entered. However, in the real world, it is often essential to moderate the entries in theguestbook, and thereby control the content displayed to other visitors. And this is where patGuestbooksmoderation feature comes in handy. In order to enable moderation, you need to navigate back to theadministration module, and select the "Voice of the People" entry from the drop-down menu at the top ofthe page. On the resulting information screen, navigate to the "General Settings" section, and check thebox for guestbook moderation. Use the "Save Changes" button to record your changes, and the job is

done! Now, whenever a user tries to enter acomment in your guestbook, a message will appear indicating that the entry will be moderated, Theadministrator - in other words, you - can then selectively approve or reject each comment via the siteadministration module. In order to do this, navigate back to the administration module, and select the"Voice of the People" entry from the drop-down menu at the top of the page. On the resulting informationscreen, navigate to the "General Settings" section and you will be presented with a list of all active and

inactive entries. You can update the status ofeach entry, and use the "Update Status" command to save your changes; all entries marked as active willnot appear in the guestbook.

Printed from DevShed.com

2

If Looks Could Kill...Next up, interface customization. As you may already know, patGuestbook is tightly integrated with asister project, patTemplate, a powerful PHP-based template engine (if you dont know how patTemplateworks, you should read the introductory material at http://www.devshed.com/Server_Side/PHP/patTemplate/patTemplate1 and only then proceed forwardwith this tutorial). This template engine makes it fairly easy to create your own skins for the patGuestbookinterface (and even share them with others, if you so desire).First things first - where are the templates located? If you recollect, this location was specified as part ofthe configuration parameters located in the "patGuestbook.php" file in your installations "config"directory:

Look inside this directory, and youll see a structure like this:

skins | --------- pat | | | ------- img | | | | | ------- styles --------- textonly | ------- img | | ------- styles

Do those directory names ring a bell? They should - theyre the template names that appear every time youcreate a new guestbook. So, if you want to create your own set of templates, this is obviously a good wayto start. Now, the patGuestbook application uses three different templates for rendering the user interface:1. patGuestbookList.tmpl - this is the template that displays the entries in the guestbook2. patGuestbookAdd.tmpl - this is the template which handles adding new entries to the guestbook3. patGuestbookDisabled.tmpl - this template simply displays an error message when a particularguestbook is disabledLets start with the "patGuestbookList.tmpl" file. To make things easier, Ill give you a quick peek at thedesired output before I explain the templates innards to you. [image]image3.jpg[/image]Now, if you take a close look at it, youll see that this is very similar to the "textonly" template - all Ivereally done is add a navigation menu to the left side of the page. Im going to call my new template "melonfire" (feel free to name your appropriately), and so my first taskis to create a directory parallel to the "pat and "textonly" folders in the "skins" directory. Under thisdirectory, Ill add an "img" directory to store images, and a "styles" directory to store stylesheets. Next up, the page layout. After much thought and coffee-napkin scrawls, I decided on a simpletwo-column layout for my guestbook, with the navigation bar in the left column and the main guestbookcontent in the right one. Heres the basic skeleton:

Printed from DevShed.com

3

http://www.devshed.com/Server_Side/PHP/patTemplate/patTemplate1

{GB_NAME}
// snip // snip

Since the menu on the left is going to be constant across all pages, it can be hardcoded into the template -here it is:

Of course, since the menu is going to be constant across the pages, you can even abstract it into anothertemplate - I leave that to you as an exercise.

Printed from DevShed.com

4

Bringing In The DatabaseAt this point, I have identified the layout for the pages, and also shown you the menu that will bedisplayed on each page. Now for the most important item - connecting all this up to the patGuestbook database.Heres the code:

Welcome to {GB_NAME}!

By {ENTRY_NAME} on {ENTRY_DATE}
{LABEL_EMAIL} : {ENTRY_EMAIL} {LABEL_HOMEPAGE} : {ENTRY_HOMEPAGE} {LABEL_ENTRY} : {ENTRY_ENTRY} Ratings {RATING_LABEL} : {RATING_VALUE}

>

Chaos, youre thinking...and rightly so. But let me help make some sense of it.1. First, the page header, displaying the name of the guestbook.

Welcome to {GB_NAME}!

{GB_NAME} is a special patGuestbook template variable that will be replaced by the name of theguestbook specified at run time - in this example, "Voice of the People".2. Next, I have to define the template used for display of each field in the guestbook. In this example, Iwould like to display the name of the user along with the time at which the entry was saved.

By {ENTRY_NAME} on {ENTRY_DATE}

Once I am done with the users name via the {ENTRY_NAME} and {ENTRY_DATE} variables, I canproceed to the users email address and URL.

{LABEL_EMAIL} : {ENTRY_EMAIL} {LABEL_HOMEPAGE} : {ENTRY_HOMEPAGE}

Once again, two special patGuestbook variables -{ENTRY_EMAIL} and {ENTRY_HOMEPAGE} - areused to retrieve the information entered by the user. I can also display the appropriate labels for each fieldvia the {LABEL_EMAIL} and {LABEL_HOMEPAGE} variables.How about displaying the heart of the guestbook - the users comments?

{LABEL_ENTRY} : {ENTRY_ENTRY}

Printed from DevShed.com

5

Finally, the rating field, which is also fairly straightforward.

Ratings {RATING_LABEL} : {RATING_VALUE}

One of the configuration variables in the guestbook is the number of entries to be displayed on a singlepage. So, I also need to add paging logic, and a link to add new entries to the system.

>

The {URL_PREVIOUSPAGE} and {URL_NEXTPAGE} variables are used to display the links to theprevious and next page, if required. the {URL_ADDENTRY} variable contains the URL that allows usersto add a new entry to the guestbook.

Printed from DevShed.com

6

A Well-Formed PlanSo that takes care of the main guestbook page - now how about customizing the input form for new entries?Heres what it should look like,

and heres the code that makes it happen:

Welcome to {GB_NAME}!

Note that this guestbook is moderated, and your entry will only appear in the list of entries once it has been approved by a moderator.

Ooops, Ive found some errors in your entries... please check them again:

{ERR_FIELD}: The email you entered is of a type unknown to the net as it exists today. Please enter something a little more comprehensible. {ERR_FIELD}: The url you entered is of a type unknown to the net as it exists today. Please enter something a little more comprehensible. {ERR_FIELD}: This field is required, but you left it utterly empty. Please feed it something to make it happy :) {ERR_FIELD}: Youve forgotten to give a rating for this item...
{LABEL_NAME} : {LABEL_EMAIL} : {LABEL_HOMEPAGE} : {LABEL_ENTRY} {ENTRY_ENTRY} {RATINGS} {RATING_LABEL} : Please select a rating... {RATING_VALUE} {RATING_VALUE}

Ugly isnt it?1. First, the page header, displaying the name of the guestbook.

Welcome to {GB_NAME}!

2. The template that displays a message to the user when moderation follows the header.

Note that this guestbook is moderated, and your entry will only appear in the list of entries once it has been approved by a moderator.

3. This is followed by a list of error messages, which are displayed when required fields are left empty.

Ooops, Ive found some errors in your entries... please check them again:

{ERR_FIELD}: The email you entered is of a type unknown to the net as it exists today. Please enter something a little more comprehensible. {ERR_FIELD}: The url you entered is of a type unknown to the net as it exists today. Please enter something a little more comprehensible. {ERR_FIELD}: This field is required, but you left it utterly empty. Please feed it something to make it happy :) {ERR_FIELD}: Youve forgotten to give a rating for this item...

Printed from DevShed.com

7

Feel free to edit the error messages above to reflect the personality and style of your site.4. Finally, the meat of the template - the form that is displayed to the user. As usual, there are pre-definedpatGuestbook templates that I can work with for this section. Remember to be careful when tweakingthese templates (unless, of course, youre comfortable with patTemplate, in which case, tweak away!).

{LABEL_NAME} : {LABEL_EMAIL} : {LABEL_HOMEPAGE} : {LABEL_ENTRY} {ENTRY_ENTRY} {RATINGS} {RATING_LABEL} : Please select a rating... {RATING_VALUE} {RATING_VALUE}

For each field in the guestbook, I have two tags - one displaying the label and the other displaying theform field to the user. For example, for the users name, Ive used the {LABEL_NAME} variable for thelabel and the {ENTRY_NAME} variable for the text box that is displayed to the user.

Printed from DevShed.com

8

When Things Go WrongFinally, patGuestbook includes a template to display an error message to the user when a particularguestbook has been specifically disabled.



Houston, we have a problem...
But, the {GB_NAME} will be back soon...

Pretty simple, this - plain ol HTML, no fancy-shmancy gimmicks or convoluted variables. In order to seewhat it looks like, turn off a guestbook from the administration module and try accessing it - you shouldsee something like this:

Thats about it for the user interface templates that can be customized. If you thought that was easy andyoure hankering for another challenge, you can always try customizing the administration module as well(alternatively, you could get up from your computer and go get yourself a life).

Printed from DevShed.com

9

Locking It DownIf there is one drawback to the patGuestbook application, it is the lack of security for the administrationmodule. By default, patGuestbook leaves the entire administration section totally unprotected and open tomalicious attacks. If youre using the Apache Web server (you probably are), you can access the serversauthentication features to add basic security to this section.In order to illustrate how this works, lets consider a simple example. Lets assume the existence of thefollowing directory structure:

/usr/local/apache/htdocs/patGuestbook/ example.php /admin/ guestbook.php

Now, lets suppose that I want to protect the directory "admin". Its fairly simple to do with HTTP authentication.The first step is to ensure that your Apache build includes support for the "mod_auth" module. You cancheck this by executing the Apache binary with the "-l" command-line option.

$ /usr/local/apache/bin/httpd -l Compiled-in modules: http_core.c mod_env.c mod_log_config.c mod_mime.c mod_negotiation.c mod_status.c mod_include.c mod_autoindex.c mod_dir.c mod_cgi.c mod_asis.c mod_imap.c mod_actions.c mod_userdir.c mod_alias.c mod_access.c mod_auth.c mod_setenvif.c mod_php4.c

If you dont see "mod_auth" in the list, youll need to recompile Apache with support for that module.Next, check Apaches configuration file, "httpd.conf", and ensure that the option

AllowOverride All

is present in the section for the server document root. This allows you to override global server settings viaper-directory ".htaccess" control files.Next, create a file named ".htaccess" in the "admin" directory, and put the following lines into it:

AuthType Basic AuthUserFile /usr/local/apache/users AuthName "patGuestbook Administration Module" Require valid-user

This tells the server that access to the "admin" directory (the directory in which the ".htaccess" file islocated) is to be controlled, and access is to be granted to users based on the username/passwordinformation in the file "/usr/local/apache/users"The final step is to create the "users" file. Change to the "/usr/local/apache" directory (or whicheverdirectory youve decided to store the user data in) and use the "htpasswd" command:

Printed from DevShed.com

10

$ htpasswd -c users john New password: **** Re-type new password: **** Adding password for user john

You can add more users to this file if you like (remember to omit the "-c" parameter for all subsequentadditions, as that parameter creates a brand-new, empty file).Remember *not* to store the "users" file in a directory under the server document root, or else malicioususers will be able to view and download the password database through a browser.Now, attempt to access the "admin" directory via your Web browser. The browser should pop up a dialogbox and prompt you for a username and password. Access to the "admin" directory will be granted only ifyou enter a correct username and password, as defined in the "users" file.Note that this is very primitive authentication, and can substantially add to the load on your Web server ifit involves a large number of users. For a more comprehensive solution, take a look at http://www.devshed.com/Server_Side/PHP/UserAuth

Printed from DevShed.com

11

http://www.devshed.com/Server_Side/PHP/UserAuth

Over And OutAnd thats about all we have time for. In this two-part article, I introduced you to patGuestbook, a PHPapplication that makes setting up a guestbook on your site as easy as clicking your way through a series ofmenus. I showed you how to create a new guestbook, configure required and optional fields, and explorerating possibilities in your guestbook. I also showed you how to moderate entries as they are added,customize the user interface via the patTemplate engine, and protect unauthorized access to yourguestbook with simple HTTP authentication.In case youd like to learn more about the topics discussed in this tutorial, take a look at the following links:The official patGuestbook Web site, at http://www.php-tools.de/Template-Based Web Development With patTemplate, at http://www.devshed.com/Server_Side/PHP/patTemplateUser Authentication With Apache And PHP, at http://www.devshed.com/Server_Side/PHP/UserAuthUntil next time...stay healthy!Note: All examples have been tested on Linux/i586 with Apache 1.3.28, PHP 4.2 and patGuestbook 1.0.Examples are illustrative only, and are not meant for a production environment. Melonfire provides nowarranties or support for the source code described in this article. YMMV!

Printed from DevShed.com

12

http://www.php-tools.de/http://www.devshed.com/Server_Side/PHP/patTemplatehttp://www.devshed.com/Server_Side/PHP/UserAuth

Bells And WhistlesAdopting A Moderate ApproachIf Looks Could Kill...Bringing In The DatabaseA Well-Formed PlanWhen Things Go WrongLocking It DownOver And Out

[Developer Shed Network] Server Side - PHP - Building a Quick-and-Dirty PHP-MySQL Publishing System.pdfBy icarus

This article copyright Melonfire 20002002. All rights reserved.

http://www.melonfire.com/

Table of ContentsRAD, Dude!.........................................................................................................................................................1

A Little Sluggish................................................................................................................................................2

A Maniac Is Born................................................................................................................................................3

The Simple Stuff..................................................................................................................................................5

An Anatomical Exploration...............................................................................................................................7

Bedtime Stories..................................................................................................................................................11

Admin Ahoy!.....................................................................................................................................................13

A List In Time...................................................................................................................................................14

Splitting Up........................................................................................................................................................16

Erasing The Past...............................................................................................................................................22

Changing Things Around.................................................................................................................................24

Game Over.........................................................................................................................................................29

Building A QuickAndDirty PHP/MySQL Publishing System

i

RAD, Dude!In the highly competitive world of Web development, there's a crying need for a toolkit that allows developersto rapidly and efficiently construct dynamic, robust and scalable Web sites. This toolkit needs to befeaturerich yet easy to use, costeffective yet labourefficient, simple yet scalable.

Now, I may be biased or a fool (or maybe even both), but I'm going to go out on a limb here and say that PHPis that toolkit.

Think about it. The language is fast, robust and scalable. It's easy to use, comes with a great manual, and isbacked by an enthusiastic user community. It comes with a rich feature set, and includes support for almostevery new technology you can think of. It is, in short, the best opensource scripting language availabletoday.

This is not just advertising. I've been using the language for over three years now, and it still amazes me withits capabilities. Constructing a dynamic, databasedriven Web site in Perl would take me a week; with PHP,I'm done in two days, and my code is cleaner, more readable and less convoluted than my best efforts in Perl.

Now, if you're a cynical software programmer, all the rhetoric in the world isn't going to convince you. You'renot going to believe a word I say until you see the truth with your own eyes. And so, over the new few pages,I'm going to demonstrate PHP's RAD capabilities by using it to build very rapidly a simple contentpublishing system for a Web site.

Before we begin, a little context. The application you're about to see was an actual development effort Iundertook a few weeks ago for an existing customer of my company. This customer already had a PHPbasedWeb site, which we'd developed a year ago; however, it now required an addition, in the form of a dynamic,databasedriven page for corporate news and press releases. I was tagged as the person to implement thisaddition.

I was told that the requirem


Recommended