Interesting Code Flow
There are a few pages in this application that could stand some explanation.
However, you should be able to follow most of them if you understand the func-
tions in the previous section.
This is a fairly lengthy page, and for good reason: it is used for adding, editing, and
deleting questions in the database. The portion of the page to be run will be deter-
mined by the values passed by forms or links. The first time through, there will be
no variables passed, so a list of the current questions will be presented along with a
form for entering a new question. Each of the links to questions that already exist
in the database looks like this:
<a href=â€ťquestions.php?question_id=2â€ť >
Chapter 9: Survey 299
When a link like this is clicked, and the questions.php script is run again, the
very last of the initial if-else tests in the setup code at the top of the file run, as
// if the ID of a question is passed in, retrieve its
// from the database for editing.
// set the form title to indicate the action the user can
$qform_title = â€˜Edit A Question : #â€™.$question_id;
Notice how you can get all the information associated with $question_id with
one function call (fetch_question()). Since fetch_question() is returning an
associative array, we can use extract() to create variables from the values in the
Next, go into this loop:
$lines = array(â€˜<b>Answers:</b><br>â€™);
// print form elements for answers to the question.
$acount = 0;
if ($question_id > 0)
$query = â€śselect answer_id, answer from answers
300 Part III: Simple Applications
where question_id = $question_id order by answer_id
$result = my_query($query);
while (list($aid,$atxt) = mysql_fetch_row($result))
// we increment the count first because we want the
// first key value to be 1, not 0, to make sure that
// the key will test as non-empty.
$lines = text_field(array(
$lines = hidden_field(array(
$lines = â€ś ($aid)<br>\nâ€ť;
This block gets the answers for the selected question and prints them out inside
text fields. Additional information is put inside hidden fields. When printed out the
result for one answer will look like this:
<input type=â€ťtextâ€ť name=â€ťanswer_textâ€ť value=â€ťAnswerâ€ť size=â€ť60â€ť >
<input type=â€ťhiddenâ€ť name=â€ťanswer_idâ€ť value=â€ť10â€ť>
When this form is submitted, $answer_text will be an array. $acount will see
that the key of the array is incremented by one for each additional form field. Note
that we need to make use of a hidden form element here, because each answer
requires three pieces of information: the answer number (1â€“10), the answer text,
and, if the answer came from the database, the primary key of the row the answer
came from. The hidden field will create an array named $answer_id. The value in
each element of that array will be the primary key of the row storing the answer.
The index of that array will be the match for the index of $answer_text. In code
the technique looks like this:
$i = 1;
Chapter 9: Survey 301
Youâ€™d know, when receiving and processing the information from this screen,
that $answer_id[$i] contains the primary key of a row, and $answer_text[$i] is
the answer text that belongs in that row.
The previous section of code will print out form elements only where an answer
exists. But you should offer blank form elements so the administrator can enter
// print out blank fields to bring us up to at least 10 answers
while ($acount < 10)
$lines = text_field(array(
â€˜nameâ€™ => â€śanswer_text[$acount]â€ť
, â€˜valueâ€™ => â€˜â€™
, â€˜sizeâ€™ => 60
$lines = hidden_field(array(
â€˜nameâ€™ => â€śanswer_id[$acount]â€ť
, â€˜valueâ€™ => 0
$lines = â€ś<br>\nâ€ť;
This will complete the form and display it, giving all the blank elements you need.
For these blank answers, the form will contain the following:
<input type=â€ťtextâ€ť name=â€ťanswer_textâ€ť value=â€ťâ€ť size=â€ť60â€ť >
<input type=â€ťhiddenâ€ť name=â€ťanswer_idâ€ť value=â€ť0â€ť><br>
In these form elements, the value of the hidden field is set to 0. That way, when
it comes time to process these form elements, the script will have something to
evaluate: If $answer_id[$i] is equal to 0, this is a new element.
If the user clicks the Save Changes button to submit this form, the preceding
chunk of code will run after handling the update of the database record for the
question itself. There will always be 10 elements to be looped through, so a for
loop works nicely.
for ($i = 1; $i <= 10; $i++)
302 Part III: Simple Applications
$atxt = (string)$answer_texts[$i];
$aid = (int)$answer_ids[$i];
If no text exists for the answer, and a value exists for the answer ID, the user has
blanked out an existing answer. So delete it from the database:
my_query(â€˜delete from answers where answer_id =
$answer = mysql_real_escape_string(cleanup_text($atxt));
// if we have no ID for the answer,
// it doesnâ€™t exist yet. create a new
// record in the answers table.
$query = â€śinsert into answers (question_id, answer)
Pay attention to the explicit casting â€” (int) â€” at the beginning of that passage.
It prevents an error when the value is 0. If the element of $answer_id is not empty
(which means it canâ€™t be equal to 0), an insert statement is run:
// if we do have an ID, the answer is already
// in the answers table. update it.
$query = â€śupdate answers
set question_id = $question_id, answer =
where answer_id = $aid
Chapter 9: Survey 303
Otherwise, if an existing answer was present, an update query will do the trick.
Most of this file is readable by humans. Our goal is to draw a qualified winner at
random from the database. First we use the weekstart() function (discussed earlier
in this chapter in the section â€śFunctions from /book/survey/functionsâ€ť) to get the
date on which the current week begins:
$weekdate = (string)array_key_value($_REQUEST,â€™weekdateâ€™,â€™â€™);
$result = my_query(â€˜select â€˜.weekstart($weekdate));
list($thisweek) = mysql_fetch_row($result);
print subtitle(â€˜Draw a winner for the week of â€˜.$thisweek);
// get a list of qualifying entries for the given week.
$query = â€śselect name, email, user_id from users
where week(create_dt) = week(â€˜$thisweekâ€™)
and year(create_dt) = year(â€˜$thisweekâ€™)
and name is not null and name != â€˜â€™
and email is not null and email != â€˜â€™ and email like â€˜%@%.%â€™
and age > 0
and country is not null and country != â€˜â€™
We then create a query that will determine who is qualified. As you can see,
weâ€™ve decided that in addition to having signed in during the last week, participants
need to have entered a name, an email address, and a legitimate age to qualify.
We created a few pages to ensure that the winner selected is notified of the exciting
news and that we issue the notification in a way that provides some security. The
security isnâ€™t much, but to make reasonably sure that the person who claims the
prize is the person we intended, we would need to make use of a login system, and
users of a silly little survey may not be interested in keeping track of yet another
The best we can do here is to try to make sure that if some immoral person sees
the claim information one week, that person will not be able to easily spoof our
system in future weeks. When we send the winner notification, we will include an
eight-character claim code. This prize can only be claimed with the code. To make
things as secure as possible, we want to make sure this code is unique and very dif-
ficult to guess.
304 Part III: Simple Applications
mt_srand ((double) microtime() * 1000000);
$claim_code = substr(md5(uniqid(rand())),0,8);
The preceding code uses the uniqueid() and md5() functions to create a string
that is very random. Thereâ€™s little for a hacker to latch onto in trying to figure out
how the string is constructed. md5() will create a string that is 32 characters long,
but that can be a bit unwieldy. So weâ€™re using substr() to limit the string to eight
The user_id, the claim code, and the week of during which the contest took
place are inserted into the winners table:
$query = â€śreplace into winners (weekdate, user_id, claim_code,
values (â€˜$weekdateâ€™, $user_id, â€˜$claim_codeâ€™, now())
The winner is sent an email containing a URL that includes a claim code that
matches one in the database: http://mydomain.com/book/survey/claim.