<<

. 52
( 132 .)



>>

$paths = explode($ps, $include_path);
$above_book = realpath(BOOK_ROOT.™/../™);
if (!in_array($above_book, $paths, TRUE))
{
$paths[] = $above_book;
}
$args = func_get_args();
foreach ($args as $newpath)
{
if ($newpath == ˜™)
Chapter 9: Survey 293

{
$newpath = $above_book;
}
elseif (strpos($newpath,™/book™) === 0)
{
$newpath = $above_book.$newpath;
}
if (!in_array($newpath, $paths, TRUE))
{
$paths[] = $newpath;
}
}
$new_include_path = implode($ps, $paths);
if (!ini_set(˜include_path™, $new_include_path))
{
die(“Could not set the ˜include_path™ configuration variable
to ˜$new_include_path™”);
}
return $new_include_path;
}

The PHP configuration variable ˜include_path™ defines a set of directories that
PHP will search through to find files included with the include() and require()
functions. (Several of the built-in file system functions, like fopen(), will also use
this path if asked politely, a nice feature.) The add_to_include_path() function
figures out where it is on the actual file system of your server and what character
your installation uses to separate directories in ˜include_path™ (a semicolon in
Windows, a colon elsewhere). This lets us add the /book directory to the include
path, even if the example code is not really in the root document directory of your
Web server. The only reason the code is in a function, by the way, is to avoid creat-
ing global variables, which is considered bad style.

INITIALIZATION CODE Having defined add_to_include_path, we promptly call
it, and then include the book/functions.php file, which sets up our reusable set of
functions:

// use the local PEAR libraries
ini_set(˜include_path™, ˜.™);
add_to_include_path(˜/book™, ˜/book/pear/PEAR™, ˜/book/classes™);

require_once(˜book/autoload.php™);

// include the core function set
if (!defined(˜no_include™))
294 Part III: Simple Applications

{
require_once(˜book/functions.php™);
}


The survey application
We™re ready to dive into the code of the survey itself now, starting as always with
our header.php file.

CODE FROM /BOOK/SURVEY/HEADER.PHP
This file is included in all the pages of the survey application.

<?php
require_once(
preg_replace(˜|/survey/.*|™,™/book.php™,realpath(__FILE__))
);

// include the function definitions for this application
// (use a path from book/survey so the include will work if we™re
// running a script in the survey/admin directory)
require_once(˜book/survey/functions.php™);

// connect to the database
my_connect(˜survey™,™joeuser™,™resueoj™);

// make sure the domain isn™t on our blocked list
check_domain();

?>

This code has been put inside an if statement as a precaution. There is no need
to reload the header once it has been loaded. We can make sure that it isn™t reloaded
by creating a constant named SURVEY_HEADER. If by chance this page were loaded
a second time, you wouldn™t have to worry that included files would be imported
more than once.
The first thing we do is include the /book/book.php file. Because the survey
header file is included by pages in the /survey/admin subdirectory, as well as the
main pages in /survey, we have to specify an absolute location for /book/book.php.
We can do this using __FILE__. __FILE__ is a PHP language construct that works
like an ordinary constant, and that always contains the full name of the current file.
After /book/book.php has run, all of our main functions are defined. Then we
load the local set of function definitions. After connecting to the database, we
check to see if we™ve blocked the user™s domain (see the following section).
Chapter 9: Survey 295

FUNCTIONS FROM /BOOK/SURVEY/FUNCTIONS
The following are useful functions used in the application.

check_domain() As mentioned earlier, this is a facility to block domains, and we
use the check_domain() function to enforce the block:

function check_domain()
{
// check to see if the user is coming from a domain that is
listed
// as currently blocked in the blocked_domains database table,
// as specified by the $_SERVER values REMOTE_HOST or
REMOTE_ADDR.
// if it is, print out an error and exit.

$remote_host =
(string)array_key_value($_SERVER,™REMOTE_HOST™,™™);
$remote_addr =
(string)array_key_value($_SERVER,™REMOTE_ADDR™,™™);
$wheres = array();
if (!empty($remote_host))
{
$wheres[] = “˜$remote_host™ like concat(˜%™,domain)”;
}
if (!empty($remote_addr))
{
$wheres[] = “˜$remote_addr™ like concat(domain,™%™)”;
}
if (count($wheres) > 0)
{
$is_blocked = 0;
$where = implode(˜ or ˜, $wheres);
$query = “select 1 as is_blocked from blocked_domains
where release_dt is null and ($where)
“;
$result = my_query($query);
list($is_blocked) = mysql_fetch_row($result);
mysql_free_result($result);
if ($is_blocked == 1)
{
// Be noncomittal.
print subtitle(˜Page unavailable.™);
exit;
}
}
}
296 Part III: Simple Applications

In order to understand this code, look more closely at the query, particularly the
like predicates. When we bring up this Web page from my ISP (att.net),
$_SERVER[˜REMOTE_HOST™] is something like this: 119.san-francisco-18-19rs.
ca.dial-access.att.net. When you block domains, you™ll be blocking the top-
level domain ” in this case att.net. And this top-level domain is what will reside
in the database. So the query will have checked on any number of wildcard charac-
ters prior to the top-level domain name.
To achieve the wildcard checking, you will need to concatenate the domain
names with the % wildcard character ” so that, for instance, the query will work
against %att.net. Doing this may seem somewhat different from using your typi-
cal like predicate. It™s another powerful technique to use with SQL.
Or, since you might not have $_SERVER[˜REMOTE_HOST™] available on your
server, you might have entered a literal IP address instead. In this case, the most
general part is the beginning of the string, rather than the end. So when we compare
the domain field to $_SERVER[˜REMOTE_ADDR™], we concatenate the % character
onto the end rather than the beginning.
Also note that the start of the select statement contains select 1 rather than
select count(*). This leads to a good way of testing if any rows meet the condi-
tion of the where clause. If the where clause matches any number of rows the query
will return a single column with the value of 1, which in the programming world
means TRUE. If no rows are returned you know the where portion of the query had
no matches.
This function is just intended to demonstrate some general techniques for check-
ing server variables and comparing them against a database. In the real world it
would be about as hacker-proof as a wet tissue.

weekstart() This function generates SQL, MySQL style, to figure out the day of the
week for a particular date. You use this in the application to pick a winner for the
current week.

function weekstart ($when=™™)
{
if (empty($when))
{
$when = ˜now()™;
}
elseif ($when != ˜create_dt™)
{
$when = “˜$when™”;
}
return “from_days(to_days($when)-dayofweek($when) + 1)”;
}

The MySQL to_days() function returns an integer of the number of days since
January 1, 1000. dayofweek() returns an integer representing the day of the week
Chapter 9: Survey 297

(Sunday equals 1, Saturday equals 7). So the portion (to_days($now)-
dayofweek($when) + 1) will return an integer representing the Sunday of the
week in question. The from_days() function will then turn that number into a
date. Here is the result of this query run on Monday August 4, 2002 (the day this
chapter was first written):

mysql> select from_days(to_days(now())-dayofweek(now()) + 1);
+------------------------------------------------+
| from_days(to_days(now())-dayofweek(now()) + 1) |
+------------------------------------------------+
| 2002-08-04 |
+------------------------------------------------+
1 row in set (0.01 sec)

Note that the value passed here can be a string representing a date, it can be
empty, or it can be a field from the users table ” namely the create_dt field.

fetch_question() This function grabs the contents of a row in the questions table
and returns them as an associative array.

function fetch_question ($question_id=0)
{
$result = my_query(
˜select * from questions where
question_id=™.(int)$question_id
);
$output = mysql_fetch_assoc($result);
mysql_free_result($result);
return $output;
}

This will return from the database all the information regarding a particular
question, based on the question_id.

fetch_user() This function grabs the contents of a row in the users table and
returns them as an associative array.

function fetch_user ($user_id=™™)
{
$result = my_query(
˜select * from users where user_id=™.(int)$user_id
);
$output = mysql_fetch_assoc($result);
mysql_free_result($result);
return $output;
}
298 Part III: Simple Applications

This function returns the result set based on a user_id.

get_answers() This function returns an array of answers associated with a ques-
tion, along with the total number of votes so far for each answer.

function get_answers($question_id=0)
{
$question_id = (int)$question_id;
$query = “select a.answer, a.answer_id, count(r.user_id) as
votes
from answers a
left join responses r on a.answer_id = r.answer_id
where a.question_id = $question_id
group by a.answer_id
having votes > 0
order by votes desc
“;
$answers = array();
$result = my_query($query);
while ($row = mysql_fetch_assoc($result))
{
$answers[] = $row;
}
mysql_free_result($result);

<<

. 52
( 132 .)



>>