You are on page 1of 20

Using Oracle with PHP

SEARCH: webmonkey the web JUMP TO A TOPIC:

Choose Topic

home / backend / databases /

Databases Using Oracle with PHP


-------------------------
by Patrick Reagan 29 Jun 2001
Print | Email
this article for free.
------------------------- Patrick Reagan is a programmer by day (at Viget Labs in Arlington, VA), and
a DJ by night. His pet project, LemurGene, is a comprehensive resource for
local (and soon, national) electronic music events.
Pages:
1 Using Oracle with PHP
2 Installing Oracle, Page 1
Configuring PHP
3 Setting the Table
4 Oracle Functions If you're like me, you've been running and experimenting with
5 Connecting to the PHP ever since the halcyon, version-3 days when you set it up
Database on your Linux machine along with Apache and MySQL. You, too,
6 Inserting a Record
hailed the addition of sessions to its ever-increasing functionality
7 Selecting Multiple
and mastered the art of SQL and database design.
Records
8 Final Tips, Notes, and
Explanations Despite all that knowledge, even the best programmers make
mistakes (well, everyone except for me), so you've probably run
into problems from time to time. Maybe you altered multiple
tables inside of a single PHP script, and Bam! data
anomalies. MySQL has been improving by leaps and bounds, but
maintaining data integrity is still an area that gives Web
developers nasty headaches.

Luckily there are other databases out there that help us keep
our data consistent, one of which is Oracle. Since Oracle
supports the use of database transactions "out of the box," it
provides some major enhancements over MySQL.

In defense of MySQL, it was initially designed to be small and


fast, so it doesn't include such enterprise-level features as
referential integrity and transactions, which prevent you from
making mistakes in code, and even help you recover in the rare
occurrence of a programming error. Support for transactions has
been added in newer releases of MySQL, but to achieve a
desired level of functionality, you still need to do some tweaking.

One of the most common reasons people choose databases such


as MySQL over Oracle is price. MySQL meets the needs of most
small to mid-sized applications at little to no cost. It also doesn't
hurt that there are so many free MySQL tutorials to hold the
hands of budding Web developers. A solution such as Oracle, on
the other hand, can set you back tens of thousands of dollars,
and how-to tutorials are harder to come by. Until now, that is.

And the price of Oracle isn't necessarily a stumbling block,


either: You can easily get a cheap or even free copy of Oracle

http://webmonkey.wired.com/webmonkey/01/26/index4a.html?tw=backend (1 of 2)16/11/2004 02:07:36 p.m.


Using Oracle with PHP

for development use.

Getting Oracle

If you have access to a fast Internet connection (and I mean fast


because we're talking over 200MB for all the necessary files),
you can download the application from the Oracle Technology
Network. To do this, you must first register for an account,
which is a simple, but lengthy, task. Just fill out their application
with your information or false information if that's your style
whatever gets you to the download section. You can download
whichever version you want, but for our purposes we'll be using
Oracle Enterprise 8i for Linux.

Alternatively, you can buy the CD from the Oracle Store for the
very reasonable price of US$40, which gives you access to the
database as well as some additional tools. This is a great option
for those who don't have blazingly fast Internet access, and it
makes it easier to install the application on additional machines.

next page

Lycos Worldwide Copyright 2004, Lycos, Inc. All Rights Reserved. Lycos is a registered trademark of Carnegie Mellon University.
About Lycos | Help | Feedback | Jobs | Advertise

Your use of this website constitutes acceptance of the Lycos Network Privacy Policy and Terms & Conditions.

http://webmonkey.wired.com/webmonkey/01/26/index4a.html?tw=backend (2 of 2)16/11/2004 02:07:36 p.m.


Using Oracle with PHP

SEARCH: webmonkey the web JUMP TO A TOPIC:

Choose Topic

home / backend / databases /

Using Oracle with PHP


Databases
-------------------------
Print | Email Page 2 Installing Oracle, Configuring PHP
this article for free.
-------------------------
Installing Oracle
Pages:
1 Using Oracle with PHP Now that we have the tools we need, it's time for the hard part:
2 Installing Oracle, actually installing the Oracle database on your server. But you're
Configuring PHP
3 Setting the Table
not entirely alone. As part of the installation, you have access to
4 Oracle Functions
documentation, plus the Linux Documentation Project offers a
5 Connecting to the convenient How-To (skip the sections that apply to earlier
Database versions of the application). Once you install the database, the
6 Inserting a Record How-To guide can also help you with some common post-
7 Selecting Multiple installation procedures.
Records
8 Final Tips, Notes, and After grappling with Oracle, the PHP configuration should be nice
Explanations and easy.

Configuring PHP

I'm sure you've installed PHP many times before, and the
installation just improves with each release, so I'm not going to
give you the blow-by-blow here. Just grab the tarball from the
PHP website or any other mirror, and go through the regular ./
configure options that you use just don't forget to add the
option for Oracle support. Use --with-oci8=[DIR] where DIR is
the directory where you installed Oracle (it will default to your
ORACLE_HOME environment variable, but I used '/usr/local/
oracle/product/8.1.7').

Now that Oracle is installed and configured, you can use the
built-in test database or any other database to which you have
access. But if you want to go any further with this tutorial, you
need to have create, insert, and select privileges on the
database you'll be using.

All set? Then let's make ourselves a sample table, something we


can use to put the PHP and Oracle partnership through its paces.

next page

http://webmonkey.wired.com/webmonkey/01/26/index4a_page2.html?tw=backend (1 of 2)16/11/2004 02:08:25 p.m.


Using Oracle with PHP

Lycos Worldwide Copyright 2004, Lycos, Inc. All Rights Reserved. Lycos is a registered trademark of Carnegie Mellon University.
About Lycos | Help | Feedback | Jobs | Advertise

Your use of this website constitutes acceptance of the Lycos Network Privacy Policy and Terms & Conditions.

http://webmonkey.wired.com/webmonkey/01/26/index4a_page2.html?tw=backend (2 of 2)16/11/2004 02:08:25 p.m.


Using Oracle with PHP

SEARCH: webmonkey the web JUMP TO A TOPIC:

Choose Topic

home / backend / databases /

Using Oracle with PHP


Databases
-------------------------
Print | Email Page 3 Setting the Table
this article for free.
-------------------------
Most companies need a place to store information about their
Pages: employees. So, for the sake of example, let's build a database
1 Using Oracle with PHP that does just that. We'll start by creating a table (using the
2 Installing Oracle, bundled SQL*Plus interpreter) called "employees":
Configuring PHP
3 Setting the Table
4 Oracle Functions
SQL> CREATE TABLE employees (
5 Connecting to the
Database
employee_id_no NUMBER(4) NOT NULL,
6 Inserting a Record
7 Selecting Multiple
Records
fname_tx VARCHAR2(64) NOT NULL,
8 Final Tips, Notes, and
Explanations lname_tx VARCHAR2(64) NOT NULL,

phone_tx VARCHAR2(14),

email_tx VARCHAR2(255),

updated_dt DATE NOT NULL,

created_dt DATE NOT NULL,

PRIMARY KEY(employee_id_no)

);

Once this table is created, we want some way to generate


unique identifiers for each of our employee records. In the real
world, you would most likely use the social security number of
each employee, but for our purposes, we'll create a sequence.

If you're familiar with MySQL, you should know all about the
AUTO_INCREMENT attribute, which automatically creates a new id
(each one building, incrementally, on the last) every time a new
record is inserted. In theory, a sequence is the same thing, but
it isn't tied to a particular table. In order to use one effectively,
you must first ask what the next id is, then insert that answer
into the appropriate table. To do this, we tell Oracle to create a
sequence for the employee_id_no field in our employees table,
like so:

SQL> CREATE SEQUENCE employee_id_no START WITH 1

http://webmonkey.wired.com/webmonkey/01/26/index4a_page3.html?tw=backend (1 of 2)16/11/2004 02:08:45 p.m.


Using Oracle with PHP

INCREMENT BY 1 MAXVALUE 9999;

This sequence is self-explanatory. It will start at 1 and increment


by 1 every time it is used. The value will not exceed 9999 to
match the size limitation of employee_id_no in the employees
table.

Now that we have our basic table, let's take a look at some
specific Oracle functions have at our disposal.

next page

Lycos Worldwide Copyright 2004, Lycos, Inc. All Rights Reserved. Lycos is a registered trademark of Carnegie Mellon University.
About Lycos | Help | Feedback | Jobs | Advertise

Your use of this website constitutes acceptance of the Lycos Network Privacy Policy and Terms & Conditions.

http://webmonkey.wired.com/webmonkey/01/26/index4a_page3.html?tw=backend (2 of 2)16/11/2004 02:08:45 p.m.


Using Oracle with PHP

SEARCH: webmonkey the web JUMP TO A TOPIC:

Choose Topic

home / backend / databases /

Using Oracle with PHP


Databases
-------------------------
Print | Email Page 4 Oracle Functions
this article for free.
-------------------------
Here's a quick rundown of the Oracle 8 functions we'll be using.
Pages: (Note: We'll be traveling at a mighty fast clip if you fall
1 Using Oracle with PHP behind, I recommend you buttress this bare-bones overview
2 Installing Oracle, with the more detailed information found on the PHP website.)
Configuring PHP
3 Setting the Table
OCILogon()
4 Oracle Functions
5 Connecting to the This establishes a connection to the database. This is what we
Database reference in any other calls that require a reference to the
6 Inserting a Record current database connection
7 Selecting Multiple
Records OCIParse()
8 Final Tips, Notes, and This prepares any queries before they are executed against the
Explanations
current database.

OCIBindByName()
This is used in conjunction with OCIParse to prepare user-
supplied data as part of our statement.

OCIExecute()
This executes a statement against the database once it is
prepared.

OCIError()
This informs us of any errors that occur with any of our queries.

OCIFetchInto()
This is used in our script to loop through any records that we
retrieve from the database.

OCICommit()
This commits a transaction to the database.

OCIRollback()
This returns the database to a previous state.

Got all that? Once you catch your breath, the next step is to go
ahead and connect to the database.

next page

http://webmonkey.wired.com/webmonkey/01/26/index4a_page4.html?tw=backend (1 of 2)16/11/2004 02:10:26 p.m.


Using Oracle with PHP

Lycos Worldwide Copyright 2004, Lycos, Inc. All Rights Reserved. Lycos is a registered trademark of Carnegie Mellon University.
About Lycos | Help | Feedback | Jobs | Advertise

Your use of this website constitutes acceptance of the Lycos Network Privacy Policy and Terms & Conditions.

http://webmonkey.wired.com/webmonkey/01/26/index4a_page4.html?tw=backend (2 of 2)16/11/2004 02:10:26 p.m.


Using Oracle with PHP

SEARCH: webmonkey the web JUMP TO A TOPIC:

Choose Topic

home / backend / databases /

Using Oracle with PHP


Databases
-------------------------
Print | Email Page 5 Connecting to the Database
this article for free.
-------------------------
To connect to our database (the "employees" table we created), we
Pages: use the OCILogon() function. It takes three parameters: your
1 Using Oracle with PHP username, password, and the database to which you connect. We use
2 Installing Oracle, a few "define" statements to set these variables. This makes it easy to
Configuring PHP change any of this information later. For security reasons, put these
3 Setting the Table defines in a separate include file that won't be served up by the Web
4 Oracle Functions server but can be accessed by any PHP script that needs it.
5 Connecting to the
Database
6 Inserting a Record Assuming that Apache is doing our serving, create a file called .
7 Selecting Multiple htaccess in the directory where this script will reside. Then add the
Records following lines to the file:
8 Final Tips, Notes, and
Explanations
<Files db-include.inc>

order allow,deny

deny from all

</Files>

Create the file db-include.inc:

<?php

define('DB_USER', 'your-username');

define('DB_PASS', 'your-password');

define('DB_NAME', 'your-database');

?>

Now we make the connection to our database using the values


supplied in 'db-include.inc':

<?

require('./db-include.inc');

http://webmonkey.wired.com/webmonkey/01/26/index4a_page5.html?tw=backend (1 of 2)16/11/2004 02:10:53 p.m.


Using Oracle with PHP

$iDBConn = OCILogon(DB_USER, DB_PASS, DB_NAME);

?>

If you happen to get an error when connecting, and you are certain
that everything your username, password, and database
information is correct, your ORACLE_HOME environment variable may
not be set. Try using this bit of code before the call to OCILogon():

<?php

putenv("ORACLE_HOME=/usr/local/oracle/product/8.1.7");

?>

In this example, we are using '/usr/local/oracle/product/8.1.7'


for the directory. This will vary between installations, so change this
line to reflect the appropriate path for your system. This is usually
identical to the directory you supplied when configuring the PHP
module.

Now it's time to get down to actually manipulating the data in our
table.

next page

Lycos Worldwide Copyright 2004, Lycos, Inc. All Rights Reserved. Lycos is a registered trademark of Carnegie Mellon University.
About Lycos | Help | Feedback | Jobs | Advertise

Your use of this website constitutes acceptance of the Lycos Network Privacy Policy and Terms & Conditions.

http://webmonkey.wired.com/webmonkey/01/26/index4a_page5.html?tw=backend (2 of 2)16/11/2004 02:10:53 p.m.


Using Oracle with PHP

SEARCH: webmonkey the web JUMP TO A TOPIC:

Choose Topic

home / backend / databases /

Using Oracle with PHP


Databases
-------------------------
Print | Email Page 6 Inserting a Record
this article for free.
-------------------------
Let's try adding an employee to the employees table. First, we assign the data we
Pages: want to some variables (this will be useful when we use the OCIBindByName()
1 Using Oracle with PHP function):
2 Installing Oracle,
Configuring PHP
3 Setting the Table <?php
4 Oracle Functions
5 Connecting to the
// data to insert into the employees table
Database
6 Inserting a Record
7 Selecting Multiple
$strFName = 'Patrick';
Records
8 Final Tips, Notes, and $strLName = 'Reagan';
Explanations
$strPhone = '703.555.2134';

$strEmail = 'foo@bar.com';

?>

Next, we build our query and substitute the appropriate placeholders for the values
we want to insert. A placeholder is an arbitrary identifier (usually it just matches
the name of the column you are addressing), prefixed by a colon (':') that is placed
in the text of the query string. This is opposed to using the dot ('.') operator to just
concatenate your data into your query string (I'll discuss the benefits of using
OCIBindByName() later).
http://webmonkey.wired.com/webmonkey/01/26/index4a_page6.html?tw=backend (1 of 5)16/11/2004 02:12:30 p.m.
Using Oracle with PHP

<?php

// build the insert statement

$qsvAddEmployee = "

INSERT INTO employees

(employee_id_no, fname_tx, lname_tx,

phone_tx, email_tx, updated_dt, created_dt)

VALUES (employee_id_no.nextval,:fname_tx, :lname_tx,

:phone_tx, :email_tx, SYSDATE, SYSDATE)";

?>

OK, so I lied: In addition to using placeholders, we also need a few other pieces of
information in the query. Let's take a look at what we're doing here.

We did, in fact, use placeholders which correspond to the column where we will be
inserting the data (i.e., the placeholder ":fname_tx" refers to the column of the
same name). Two things that I have added were the use of "employee_id_no.
nextval" and SYSDATE in the values list. The first actually refers to the sequence we
created earlier, rather than a column in the employees table. The first part of this
statement ("employee_id_no") refers to the sequence name, while the second part
("nextval") tells Oracle to increment the sequence using its INCREMENT_BY value.

Using the keyword SYSDATE tells Oracle that we want to use today's date and time
as the value for both the date the record was created and when it was updated. In
this instance, it isn't necessary to provide placeholders since we won't be supplying
any user-defined data.

After we prepare the query, we call OCIParse() with the connection identifier
established from OCILogon() and the query string we just created. OCIParse() will
return a statement identifier that we will use later on.

<?php

http://webmonkey.wired.com/webmonkey/01/26/index4a_page6.html?tw=backend (2 of 5)16/11/2004 02:12:30 p.m.


Using Oracle with PHP

$iStatement = @OCIParse($iDBConn, $qsvAddEmployee);

?>

Once we have the statement identifier, we can use it to check for any errors that
came back when attempting to parse the query string. Since OCIError() returns
an associative array, we need to check the "code" element to see if an error code
was returned. If so, we can use OCIRollback() to back out any incremental
changes that were made to the database:

<?php

// verify that SQL is valid

$arrError = OCIError($iStatement);

if ($arrError['code'])

print $arrError['message'];

OCIRollback($iDBConn);

exit;

?>

In this example, a rollback won't do anything particularly useful, but when you're
updating multiple tables in one script, it can be a lifesaver. Note that even though
we roll back any changes to the database, the sequence will retain its current
incremented value. This ensures that a number in a sequence will never be reused,
as that would be catastrophic to our insert script.

Going back to the example where we build our insert statement using named
placeholders, next we come to the part where we use OCIBindByName() to bind a
specific value to each placeholder. OCIBindByName() requires us to provide it with
a statement identifier from our previous OCIParse() call, the placeholder string to
which we want to bind our variable, the variable we are binding, and the maximum
http://webmonkey.wired.com/webmonkey/01/26/index4a_page6.html?tw=backend (3 of 5)16/11/2004 02:12:30 p.m.
Using Oracle with PHP

size of the field we are addressing. We can see the usage here:

<?php

// bind variables to our placeholders

@OCIBindByName($iStatement, ':employee_id_no', &$iEmployeeId, 4);

@OCIBindByName($iStatement, ':fname_tx', &$strFName, 64);

@OCIBindByName($iStatement, ':lname_tx', &$strLName, 64);

@OCIBindByName($iStatement, ':phone_tx', &$strPhone, 14);

@OCIBindByName($iStatement, ':email_tx', &$strEmail, 255);

?>

This provides us with two major benefits. First, it gets rid of the problem of having
to use addslashes() to escape any single quotes in our data, and it also inserts a
NULL into a column rather than the empty string.

Finally, we use OCIExecute() to execute the query after all variables have been
bound. We call it with OCI_DEFAULT as a parameter, which tells Oracle not to auto-
commit the data on success. Again, this doesn't do a whole lot for us in this
example, but would come in handy when updating multiple tables. Once we've
confirmed that the insert was successful, we call OCICommit() to save the changes
to the database.

<?php

// execute the query

@OCIExecute($iStatement, OCI_DEFAULT);

// make sure there were no errors with our query

$arrError = OCIError($iStatement);

http://webmonkey.wired.com/webmonkey/01/26/index4a_page6.html?tw=backend (4 of 5)16/11/2004 02:12:30 p.m.


Using Oracle with PHP

if ($arrError['code'])

print $arrError['message'];

OCIRollback($iDBConn);

exit;

OCICommit($iDBConn);

?>

OK, now that our new employee information is in the table, how do we get it? In
the next section, we'll go over the use of SELECT statements with Oracle.

next page

Lycos Worldwide Copyright 2004, Lycos, Inc. All Rights Reserved. Lycos is a registered trademark of Carnegie Mellon University.
About Lycos | Help | Feedback | Jobs | Advertise

Your use of this website constitutes acceptance of the Lycos Network Privacy Policy and Terms & Conditions.

http://webmonkey.wired.com/webmonkey/01/26/index4a_page6.html?tw=backend (5 of 5)16/11/2004 02:12:30 p.m.


Using Oracle with PHP

SEARCH: webmonkey the web JUMP TO A TOPIC:

Choose Topic

home / backend / databases /

Using Oracle with PHP


Databases
-------------------------
Print | Email Page 7 Selecting Multiple Records
this article for free.
-------------------------
The tactic we use to retrieve the data in the "employees" table is pretty similar to the one we
Pages: used to insert the data. We go through the same steps of building the query, parsing the query,
1 Using Oracle with PHP and checking for errors, but then we loop through all the records returned from the query. To
2 Installing Oracle, do this, we eliminate the use of OCIBindByName(), OCIRollback(), and OCICommit().
Configuring PHP
3 Setting the Table
4 Oracle Functions
<?php
5 Connecting to the
Database
// now select back our data
6 Inserting a Record
7 Selecting Multiple
Records $qsvGetEmployees = "
8 Final Tips, Notes, and
Explanations SELECT employee_id_no,

fname_tx,

lname_tx,

phone_tx,

email_tx

FROM employees

ORDER BY lname_tx ASC,

fname_tx ASC";

http://webmonkey.wired.com/webmonkey/01/26/index4a_page7.html?tw=backend (1 of 3)16/11/2004 02:13:18 p.m.


Using Oracle with PHP

$iStatement = @OCIParse($iDBConn, $qsvGetEmployees);

@OCIExecute($iStatement, OCI_DEFAULT);

// check our query

$arrError = OCIError($iStatement);

if ($arrError['code'])

print $arrError['message'];

exit;

?>

<table border="1" width="600">

<tr>

<td>ID</td>

<td>FIRST NAME</td>

<td>LAST NAME</td>

<td>EMAIL</td>

<td>PHONE</td>

</tr>

<?

// loop through our records and display each one

// OCI_ASSOC tells OCIFetchInto to return an array keyed off of the field

// names instead of index (not the default behavior)

while (OCIFetchInto($iStatement, &$arrEmployee, OCI_ASSOC+OCI_RETURN_NULLS))

{
http://webmonkey.wired.com/webmonkey/01/26/index4a_page7.html?tw=backend (2 of 3)16/11/2004 02:13:18 p.m.
Using Oracle with PHP

?>

<tr>

<td><?= $arrEmployee['EMPLOYEE_ID_NO'] ?></td>

<td><?= $arrEmployee['FNAME_TX'] ?></td>

<td><?= $arrEmployee['LNAME_TX'] ?></td>

<td><a href="mailto:<?= $arrEmployee['EMAIL_TX'] ?>">

<?= $arrEmployee['EMAIL_TX'] ?></a></td>

<td><?= $arrEmployee['PHONE_TX'] ?></td>

</tr>

<? }?>

</table>

Note that this code takes care of all the database interaction and also generates a tabular
display of each record in the employees table just one example of how we can embed HTML
in a PHP script.

That should be enough to get you rolling. But before I set you free to explore on your own, let's
tie up a few loose ends.

next page

Lycos Worldwide Copyright 2004, Lycos, Inc. All Rights Reserved. Lycos is a registered trademark of Carnegie Mellon University.
About Lycos | Help | Feedback | Jobs | Advertise

Your use of this website constitutes acceptance of the Lycos Network Privacy Policy and Terms & Conditions.

http://webmonkey.wired.com/webmonkey/01/26/index4a_page7.html?tw=backend (3 of 3)16/11/2004 02:13:18 p.m.


Using Oracle with PHP

SEARCH: webmonkey the web JUMP TO A TOPIC:

Choose Topic

home / backend / databases /

Using Oracle with PHP


Databases
-------------------------
Print | Email Page 8 Final Tips, Notes, and Explanations
this article for free.
-------------------------
Throughout this tutorial, you may have noticed that we've
Pages: preceded many of the OCI* functions with the "@" character. By
1 Using Oracle with PHP doing this, we suppress any warnings that may be output to the
2 Installing Oracle, browser in the event of an error. Since we're using OCIError()
Configuring PHP to gracefully handle any errors that occur, we don't want any
3 Setting the Table ugly warning text to appear.
4 Oracle Functions
5 Connecting to the
Also note that when we use OCIFetchInto(), the associative
Database
6 Inserting a Record
array that is returned is keyed off the name of the field. Even
7 Selecting Multiple
though we provided lowercase field names in our query, the
Records keys in the array are all uppercase.
8 Final Tips, Notes, and
Explanations Where To Go from Here?

Now that you have your environment all set up and you know
how to insert data into, and select data from, the database, I'm
sure you can see how easy it is to make updates and deletions.
While this code works well for this particular example, you can
extend the functionality to be more general.

My company recently created a recipe and cooking-related


website called CleverChef. It was through building this site that I
honed my knowledge of PHP and Oracle integration, and I
learned the value of Object-Oriented Programming in PHP. By
placing all our database code within class methods, we no longer
had to embed SQL queries in every page, which meant that any
time there was a change to the schema, the code on the site
could be updated very quickly since there was only one file that
queried the database directly. Thanks to this separation of
scripts and database, the coding and site maintenance for large
sites is much, much easier. Why don't you try and set up one of
your own?

To get you started down this path, I suggest you create a class
that corresponds to each of your tables, as well as one that
manages information about your database connection. As you
think of method names for each of your classes, consider things
like createConnection(), insert(), update(), delete(), and
other actions you would perform on a database table.

If you continue to tinker with Oracle and PHP, you'll soon learn
their strengths and weaknesses. And with any luck, you might

http://webmonkey.wired.com/webmonkey/01/26/index4a_page8.html?tw=backend (1 of 2)16/11/2004 02:14:09 p.m.


Using Oracle with PHP

just find yourself getting added to some other company's


"employees" table hopefully with a whooping dollar amount
sitting in the "salary" field.

Lycos Worldwide Copyright 2004, Lycos, Inc. All Rights Reserved. Lycos is a registered trademark of Carnegie Mellon University.
About Lycos | Help | Feedback | Jobs | Advertise

Your use of this website constitutes acceptance of the Lycos Network Privacy Policy and Terms & Conditions.

http://webmonkey.wired.com/webmonkey/01/26/index4a_page8.html?tw=backend (2 of 2)16/11/2004 02:14:09 p.m.

You might also like