PHP Chapter 13 (Manipulate a MySQL Database)

Open and Close a connection to a database with PHP

In this section, you'll see how to manipulate the simple Address Book database you've just created. Using PHP code, you'll first open the database. Once the database is open, you can then read its contents. You'll also need to know how to add new records, and delete records. First, though, a database has to be opened, before you can do anything with it.

Opening a Connection to a MySQL database

PHP has a lot of inbuilt functions you can use to manipulate databases. In PHP version 5, a lot more were added as well! Here, we'll stay with the inbuilt functions for versions earlier than PHP 5. But if you have version 5, it's well worth researching the newer database functions. A good place to start is php.net. To open our Address Book database, we'll use the following inbuilt functions:
mysql_connect()
mysql_select_db()
mysql_close()
The approached we'll take has three steps:
  1. Open a connection to MySQL itself
  2. Specify the database we want to open
  3. Close the connection
Let's do Step 1 on the list.

Step 1 - Open a connection to MySQL

The first job is to actually connect to MySQL. As it's name suggests, mysql_connect( ) does exactly that. Here's the code we're going to be using. But this is just to get your started. It is recommended that you don't dash off and use this on the internet! This is for learning purposes only.
<?PHP
$user_name = "root";
$password = "";
$database = "addressbook";
$server = "127.0.0.1";
mysql_connect($server, $user_name, $password);
print "Connection to the Server opened";
?>
Save your work and try it out on your server (this assumes that you have the Address Book database we created earlier, and that it is in the data folder of MySQL. If you don't, you can download all the files here.).
The first four lines are just setting up variables, and putting something in them:
$user_name = "root";
$password = "";
$database = "addressbook";
$server = "127.0.0.1";
The username we're trying here is "root" and the password is blank. These are the MySQL defaults. You don't need to change these, in most cases.
Hopefully, you won't have any errors. But the line that connects to MySQL is this:
mysql_connect($server, $user_name, $password);
So you type the name of the function first ( mysql_connect ), followed by the round brackets. In between the round brackets, you need three things: the name of your server, your MySQL username, and your MySQL password. These can be entered directly, like this:
mysql_connect( '127.0.0.1', 'root', '' );
Or as variables, like we did at first:
$user_name = "root";
$password = "";
$server = "127.0.0.1";
mysql_connect($server, $user_name, $password);
And that's all you need to get you connected to MySQL. But we haven't connected to the database yet. That's Step 2 on our list.

Step 2 - Specify the database you want to open

In our code, we set up a variable with the name of our database:
$database = "addressbook";
We now need to do something with this variable. So add this new line to your code (the new line is in blue text):
$user_name = "root";
$password = "";
$database = "addressbook";
$server = "127.0.0.1";
mysql_connect($server, $user_name, $password);
$db_found = mysql_select_db($database);
print "Connection to the Server opened";
You use the mysql_select_db( ) function to specify which database you want to open. The function then returns a true/false value. If it finds your database, a value of true is returned; if your database can't be found then a value of false is returned. You can use some logic to test if the database was found. Change the last two lines of your code to this:
$db_found = mysql_select_db($database);
if ($db_found) {
print "Database Found";
}
else {
print "Database NOT Found";
}
Now change the database name from this:
$database = "addressbook";
to something like this:
$database = "addressbook2";
Run your code again, and you should see Database NOT Found printed out (unless you have a database called addressbook2). Change the database name back to addressbook.
But there's another option you can use for mysql_select_db - something called a resource link identifier. It's just a file handle that you used in an earlier section (opening text files). You use it like this:
$user_name = "root";
$password = "";
$database = "addressbook";
$server = "127.0.0.1";
$db_handle = mysql_connect($server, $user_name, $password);
$db_found = mysql_select_db($database, $db_handle);
if ($db_found) {
print "Database Found " . $db_handle;
}
else {
print "Database NOT Found " . $db_handle;
}
So when we connect to the database, we're now using this:
$db_handle = mysql_connect($server, $user_name, $password);
It's just the same as before, except we're returning a value from the mysql_connect function, and putting it into a variable called $db_handle. When we connect to the database, we can use this file handle:
$db_found = mysql_select_db($database, $db_handle);
The resource link identifier (file handle) goes after the name of the database you want to open. You can then use this file handle to refer to your database connection.
Now that we've connected to MySQL, and connected to a database, it's time to close the connection.

Step 3 - Close the connection

Closing a connection to a database is quite easy. If you've used a file handle, as above, you just do this:
mysql_close( $db_handle );
Otherwise, you don't need to bother. It's recommended that you take the file handle approach, though. That's what we'll be doing from now on.
So, we'll add a line to close our connection. Here what your code should now look like:
<?PHP
$user_name = "root";
$password = "";
$database = "addressbook";
$server = "127.0.0.1";
$db_handle = mysql_connect($server, $user_name, $password);
$db_found = mysql_select_db($database, $db_handle);
if ($db_found) {
print "Database Found ";
mysql_close($db_handle);
}
else {
print "Database NOT Found ";
}
?>
Now that we've got a connection to the database, it's time to look at how you can access the data in the database.

MySQL databases - read a record with PHP

To read records from a database, the technique is usually to loop round and find the ones you want. To specify which records you want, you use something called SQL. This stands for Structured Query Language. This is a natural, non-coding language that uses words like SELECT and WHERE. At it's simplest level, it's fairly straightforward. But the more complex the database, the more trickier the SQL is. We'll start with something simple though.
What we want to do, now that we have a connection to our database, is to read all the records, and print them out to the page. Here's some new code, added to the PHP script you already have. The new lines are in blue:
<?PHP
$user_name = "root";
$password = "";
$database = "addressbook";
$server = "127.0.0.1";
$db_handle = mysql_connect($server, $user_name, $password);
$db_found = mysql_select_db($database, $db_handle);
if ($db_found) {
$SQL = "SELECT * FROM tb_address_book";
$result = mysql_query($SQL);
while ( $db_field = mysql_fetch_assoc($result) ) {
print $db_field['ID'] . "<BR>";
print $db_field['First_Name'] . "<BR>";
print $db_field['Surname'] . "<BR>";
print $db_field['Address'] . "<BR>";
}
mysql_close($db_handle);
}
else {
print "Database NOT Found ";
mysql_close($db_handle);
}
?>
Before we go through the new code to see what's happening, run your script. You should find that the address you added in a previous section is printed out. (We only have one record at the moment.)
1
Test
Name
12 Test Street
The first line in the new code is this:
$SQL = "SELECT * FROM tb_address_book";
The $SQL is just a normal variable. But we're putting into it a long string. This is a SQL statement. Here's a brief run down on SQL.

Structured Query Language

SQL (pronounced SEEKwel), is a way to query and manipulate databases. The basics are quite easy to learn. If you want to grab all of the records from a table in a database, you use theSELECT word. Like this:
SELECT * FROM Table_Name
SQL is not case sensitive, so the above line could be written:
Select * From Table_Name
But your SQL statements are easier to read if you type the keywords in uppercase letters. The keywords in the lines above are SELECT and FROM. The asterisk (*) means "All Records".Table_Name is the name of a table in your database. So the whole line reads:
"SELECT all the records FROM the table called Table_Name"
You don’t have to select all the records from your database. You can just select the columns that you need. For example, if we wanted to select just the first name and surname columns from this table, we can specify that in our SQL String:
"SELECT First_Name, Surname FROM tb_address_book";
When this SQL statement is executed, only the First_Name and Surname columns from the database will be returned.
There are a lot more SQL commands to get used to, and you'll meet more of them as you go along. For now, we're just selecting all the records from our table.

Back to the Code

The first line of our code, then, was this:
$SQL = "SELECT * FROM tb_address_book";
SO we have a SQL statement, but we need to pass it to another inbuilt function:
mysql_query( )
The mysql_query( ) function is used to send a SQL query to your database. If you have typed out your SQL correctly, then the function will return a value. This value will be true, false, or a file handle. Because we're using the SELECT keyword, the value returned by will be a file handle. In our code, the line was this:
$result = mysql_query( $SQL );
The file handle returned in our $result variable just points to the results. It doesn't actually bring anything back. To bring back the data, we had this inside a while loop:
$db_field = mysql_fetch_assoc( $result );
The inbuilt function we're using to bring results back is this:
mysql_fetch_assoc( $result )
The assoc part means Associative. As in "associative array". So we're asking that the results be brought back in an array format. In between the round brackets of mysql_fetch_assoc we have typed the name of our file handle – the one that was pointing to the results of SQL statement.
Remember: an associative array is one where the keys are text. So it's this format:
Array['One'] =
Array['Two'] =
Array['Three]' =
And not this:
Array[1] =
Array[2] =
Array[3] =
When the mysql_fetch_assoc function returns an array, we're putting it all into a variable called$db_field. The Key part of the array is all the Column names from our database tables. This is done automatically for you. So the array format will be this:
$db_field[Column_Name] = Value
The reason why you're doing this is so that you can loop round the array and access the values from the table. Here's our loop, without anything between the round brackets:
while ( ) {
print $db_field['ID'] . "<BR>";
print $db_field['First_Name'] . "<BR>";
print $db_field['Surname'] . "<BR>";
print $db_field['Address'] . "<BR>";
}
So we're printing whatever the value is in the array position $db_field['ID'],$db_field['First_Name'], $db_field['Surname'] and $db_field['Address']. We're also adding a HTML line break at the end, just for printing purposes.
If all that is confusing, just remember the format:
Array_Name[Table_Coulmn_Name] = Value_From_Record
Our whole while loop, then, is this:
while ($db_field = mysql_fetch_assoc($result) ) {
print $db_field['ID'] . "<BR>";
print $db_field['First_Name'] . "<BR>";
print $db_field['Surname'] . "<BR>";
print $db_field['Address'] . "<BR>";
}
Because that is a bit complex, let's go through the steps we've used to access the records from our table:
  1. Set up a SQL Statement that can be used to get the records from the database table
  2. Use mysql_query() to bring back the records we've specified in Step 1
  3. Use mysql_fetch_assoc() to set up an array. The array will contain all the records that were returned in Step 2
  4. Loop round all the data in the array using a While loop
Step 1 was this, in the code:
$SQL = "SELECT * FROM tb_address_book";
Step 2 was this:
$result = mysql_query($SQL);
Step 3 was this:
$db_field = mysql_fetch_assoc($result)
And Step 4 was this:
while ($db_field = mysql_fetch_assoc($result) ) {
print $db_field['ID'] . "<BR>";
print $db_field['First_Name'] . "<BR>";
print $db_field['Surname'] . "<BR>";
print $db_field['Address'] . "<BR>";
}
If you're still confused, study the code and go over this section.

Add a record to a MySQL database

To add records to a table in your database, you use more or less the same code as previously. The only thing that needs to change is your SQL statement. The steps we're going to be taking are these:
  1. Open a connection to MySQL
  2. Specify the database we want to open
  3. Set up a SQL Statement that can be used to add records to the database table
  4. Use mysql_query( ) again, but this time to add records to the table
  5. Close the connection

Set up a SQL Statement to add records to the database

In our previous script, we used some SQL to grab records from our Address Book database table. We then used a While loop to print all the records out. Because we're now going to be adding records to the Address Book table, we need some different SQL. Here's the script. The new line is in blue (The double and single quotes need to be entered exactly, otherwise you'll get errors when you run the code):
<?PHP
$user_name = "root";
$password = "";
$database = "addressbook";
$server = "127.0.0.1";
$db_handle = mysql_connect($server, $user_name, $password);
$db_found = mysql_select_db($database, $db_handle);
if ($db_found) {
$SQL = "INSERT INTO tb_address_book (First_Name, Surname, Address) VALUES ('bill', 'gates', 'Microsoft')";
$result = mysql_query($SQL);
mysql_close($db_handle);
print "Records added to the database";
}
else {
print "Database NOT Found ";
mysql_close($db_handle);
}
?>
You met all of this code from the previous section. The only difference is the new SQL statement! What the code does is to set up some variables, open a connection to the database, and then execute the SQL query. Let's have a look at the new, and rather long, statement.

INSERT INTO … VALUES

To add records to your database, you can use the INSERT statement. There are plenty of ways to use this statement, but we'll stick with something simple: adding
new values to all of our table columns.
You start by typing the words "INSERT INTO". This can be in any case you like: upper, lower or a mix. It's easier for you to read if it's in uppercase letters.
The next thing you need is the name of a table to insert your new values into. For us, this is the table that we've called tb_address_book.
Following the name of your table, type a pair of round brackets. Inside the round brackets, you can type the names of the columns in your table:
INSERT INTO tb_address_book (First_Name, Surname, Address)
Notice how we haven't included the ID column from our table. That's because the ID column was the one we set up to be an auto-incrementing number. We don't need to worry about this column because MySQL will take care of adding 1 to this field for us.
Now that you've specified which table you want to insert values into, and specified your column names, you can add the values you want to insert.
To add values, you type the word "VALUES" after the round brackets of your column names:
INSERT INTO tb_address_book (First_Name, Surname, Address) VALUES
After the word "VALUES", you type another pair of round brackets. Inside of these brackets, you can type your values. Each value should be separated by a comma. You can use either direct text, like we've done, or variables. You can even get these values straight from your HTML form, which we'll see how to do later.
So our whole line reads:
$SQL = "INSERT INTO tb_address_book (First_Name, Surname, Address) VALUES ('bill', 'gates', 'Microsoft')";
Notice how we've surrounded all of our text with double quotes. But inside of the round brackets of VALUES, we've used single quotes.
The syntax is really this (The SQL keywords are in bold):
INSERT INTO table_name ( Columns ) VALUES ( values for columns)
But try your code out now, and see if it's all working properly. You should find that you now have two records in your database table.
Exercise
Replace the values 'bill', 'gates', and 'Microsoft' with values of your own. Run your script again to add your new record to the database. Now run your other script to read the values back out.

PHP Magic Quotes

You can use a HTML form to query your databases. But there are special security considerations you need to bear in mind. We'll look at those issues in this section.

If you use things like text boxes and text areas on your forms, you need to take care. This is because of an attacks like SQL injection. Things like single quotes need to be escaped. But you can use an inbuilt PHP function for this:
mysql_real_escape_string( )
We'll see how this works in a moment, but let's get some practical work done. There is a file amongst the ones you downloaded called magicTest.php (in the scripts folder). Load this script in your browser, with your server running. You should see a text box and a button. Typed the following name into the text box:
O'Connor
Now click the button. You should see the name printed exactly as it is in the text box.
So far, so good. Now, try this.
When you installed your server, there will be a file called php.ini. This is a list of all the various settings to do with PHP itself. Locate this file called php.ini (in the folder called apache, or do a search for it). Open it up in a text editor. Search for this line:
magic_quotes_gpc = Off
Change the Off to On, if it's not already on. Then save the changes.
Now load up the your PHP script with the text box and the button. With O'Connor still in the text box, click your button again. You should see this printed:
O \' Connor
So PHP has put a backslash before the single quote. But what's going on?
Characters like single and double quotes can be very dangerous, if you're running SQL on your databases tables. These characters can be used to launch a SQL injection attack. So the makers of PHP came up with a function called magic_quotes_gpc. If this is set to On, then PHP will add the backslash to all single and double quotes. That way, an attacker's life is made more difficult. As an example, we'll load up a database and a script. These are already prepared for you.
Amongst the files you downloaded there is a folder called databases. Inside this folder there is a one called membertest. Save the entire membertest folder to your data directory in your mysql data folder. For Wampserver users this will be at:
C:\wamp\bin\mysql\mysql5.5.8\data
(If you have an earlier or later version, the number will be different.)
Now set magic_quotes_gpc = On back to magic_quotes_gpc = Off in your php.ini file.
Along with the database folder there is a PHP script called magicTest2.php (in the scripts folder). We'll use this script, and the database, to teach you about SQL injection. Not so that you can launch your own attacks, of course! It's so that you can thwart them.

PHP and SQL injection

When you open the magicTest2.php page in your browser, you'll see three textboxes: one for a username, one for a password, and one for an email address. There is also a button on the form.
Enter the following in the email address text box:
test1@test1.com
Click the button, and you should see following print out:
1
test1
test1
test1@test1.com
These correspond to the four fields in the database. The four fields are:
ID
username
password
email
So the username is test1, the password is test1, and the email address is test1@test1.com.
Now, suppose you were naïve enough to have a database table exactly like that one. An attacker will test to see if any syntax error messages can be returned. If so, this means that the author of the script has not dealt with single/double quotes correctly. The attacker can then go ahead with further probes.
Try your script again. Only this time, add a single quote to the end of the test email address in the textbox:
test1@test1.com'
Now click the Submit button. What you should find is that an error message is indeed returned. Something like this:
Warning: mysql_fetch_assoc(): supplied argument is not a valid MySQL result resource
Because Magic Quotes are off, that single quote is not being escaped. The line in our new script that is doing the damage is the one:
$SQL = "SELECT * FROM members WHERE email = '$email' ";
The SQL this time has a WHERE clause added. The WHERE clause is used when you want to limit the results to only records that you need. After the word "WHERE", you type a column name from your database (email, in our case). You then have an equals sign, followed by the value you want to check. The value we want to check is coming from the variable called $email. This is surrounded with single quotes.
When an email address is entered in the text box on our form, this value goes straight into the variable without any checks. When you type that extra single quote on the end, that will be added to the SQL. This is then run on the database. Because it's a stray single quote, you'll get a syntax error. It's this syntax error that an attacker is looking for.
Next, the attacker will try to add some SQL to yours. Try this. In the email address textbox, type the following. Type it exactly as it is, with the single quotes:
hi' OR 'x'='x
When you click the Submit button, you should find that there are no errors, and that the username, password and email address are printed out!
The attacker is trying to find out whether or not the SQL can be manipulated. If the answer is yes, further attacks will be launched. Can the table and field names be guessed? Can a username and password be guessed? It's this kind of attack that you want to thwart.
Try this last one. Enter the following into the email address box:
' OR ''='
Now click Submit.
Again, the details are printed out. This is because an OR clause has been added. The OR clause is set to a blank string. Meaning that the records will be brought back if it's a valid email address or not!
To stop this kind of attack, you MUST use some inbuilt PHP functions. The one to use for this kind of attack is:
mysql_real_escape_string( )
Between the round brackets, you type the string you need to check, followed by an optional database handle. To test this out, there is another script like the one you've just tried. This one is called magicTest3.php (in the same scripts folder). If you open this up in your text editor, you should see this added to the code:
$email = mysql_real_escape_string($email, $db_handle);
Now, the $email variable is being checked for any of the following:
\x00
\n
\r
\
'
"
\x1a
If any of the above characters are found, a backslash is added. Try the new script. Enter the following in the email address text box (with the single quote on the end):
test1@test1.com'
What you should find is that the following gets returned:
test1@test1.com\'
So the single quote has had a backslash added to it. The point is that the dangerous SQL doesn't get executed. Try the above attacks again. This time, you shouldn't be able to get in, if any of the listed escape characters have been used.
But you need to use the function on all variables or data that will be used in your SQL. So you should do this kind of thing:
$username = mysql_real_escape_string($username, $db_handle);
$password = mysql_real_escape_string($password, $db_handle);
$email = mysql_real_escape_string($email, $db_handle);
Examine the code in the new script. Pay attention to where the new lines go: after you have opened a connection to your database.
The PHP manual recommends the following sample script, when working with SQL (all comments are theirs; bold and colour is ours):
The PHP manual script
We have adapted the magicTest3 script, with the recommended code added, so that you can see it in action. The new script is magicTest4.php. Open the script and study the code. See if you can figure out how the new additions work.
As well as using mysql_real_escape_string( ), you'll need to use the other function you saw earlier, in the forms section - htmlspecialchars().
It can be a lot of work, dealing with SQL injection attacks, and handling all those escape characters. But if you don't want your databases attacked, you HAVE to defend yourself!

Limit the charcters that a user can enter

Another security technique that some advocate is to limit the characters that can be entered. For example, you might have this in your script:
$valid_chars = "abcdefghijklmnopqrstuvwxyz";
$valid_nums = "1234567890";
$valid_other = "£$^&_@#~";
You can then use some Conditional Logic to test if the character the user entered was on your list. If it's not, then you can display an error message.
An excellent walkthrough of security blunders can be found at:

Creating Tables with SQL and PHP

You can create tables using SQL (and whole databases), and specify the fields you want to go in the table. However, doing it this way is not recommended: you tend to forget which fields are in the table, their data types, which field is the primary keys, and which ones are set to NULL values. If you can get to grips with visual tools like phpMyAdmin then so much the better.
To create a table then, you use the CREATE keyword (known as a clause, in database speak). Here's the SQL to create the simple address book we've been using. This assumes that the database itself already exists, and that the PHP code to open a connection has already been written (you saw how to do this in a previous section):
$SQL="CREATE TABLE AddressBook
(
ID int(7) NOT NULL auto_increment,
First_Name varchar(50) NOT NULL,
Surname varchar(50) NOT NULL,
email varchar(50),
PRIMARY KEY (ID),
UNIQUE id (ID)
)";
mysql_query($SQL);
So you start with the Clause CREATE TABLE. Then you type the name of the table you want to create. In between round brackets, you type the name of your table Columns, followed by some formatting. In the code above, the first field being set up is this:
ID int(7) NOT NULL auto_increment,
The column name will be ID. The data type is an integer that is no longer that 7 digits. NOT NULL means you want something in this field, and that it can't be left blank. The ID number will be auto incremented, whenever a new record is added. Notice that there is only one comma in the line. The comma separates each field you want to create in your table.
We're also setting up three other columns here: First_Name, Surname, and email. First_Name and Surname can't be left blank ( NOT NULL), but email can be.
At the end, we have these two lines:
PRIMARY KEY (ID),
UNIQUE id (ID)
The primary key is used for things like joining data from one table to the data from another. We've set this to our ID field. Primary keys don't have duplicate values, so we've set this to be a UNIQUE field.
Once you've written your SQL statement, you can go ahead and execute it:
mysql_query($SQL);
Creating tables like this means a lot of extra, tricky work for you as a PHP programmer. If you can use a tool to do the job for you, then your coding life gets easier!

Update a MySql record with PHP

You can also update a record in your table. Not surprisingly, the word UPDATE is used for this. Here's an example:
$SQL = "UPDATE AddressBook SET email = 'new_email_address' WHERE First_Name = 'Bill'AND Surname = 'Gates'";
After the word UPDATE, you need the name of the table you want to update. Then you need another Keyword: SET. After the word SET, you type the name of the Column you want to change. In the SQL above, we're changing the email column. But notice the WHERE clause. We've specified that the record to change should have the First_Name of Bill and the Surname of Gates.
You can also update an entire column, and change all the values:
UPDATE AddressBook SET Surname = LOWER(Surname);
Again, we've specified that the AddressBook table should be updated. We've SET the column name as Surname. After an equals sign, we've used the inbuild SQL function LOWER( ). This changes a value to lower case letters. In between the round brackets of the function, we've typed the column name again. This will ensure that all the text in the Surname column gets changed to lower case.

Delete a record in a MySql table with PHP

If you want to delete a record in a table, use the DELETE Keyword. Like this:
$SQL = "DELETE FROM AddressBook WHERE First_Name = 'Bill' AND Surname = 'Gates'";
After the DELETE word, you need FROM. Then you type the name of the table. Next, you need to specify which record you want to delete. It's a good idea to make sure your WHERE clause is going to be a unique value. In the code above, we might have more than one Bill Gates in the table. If we do, everybody called Bill Gates will be deleted! A better solution is to use a unique field from your table, such as an ID field:
$SQL = "DELETE FROM AddressBook WHERE ID = '7' ";
Now, only the record that has number 7 in the ID field will be deleted.

Using WHERE to limit data in MySql and PHP

You can add a WHERE part to your SQL. But before you do, make sure you read the security section.
Using WHERE limits the records returned from a SQL statement. Most of the time, you don't want to return all the records from your table. Especially if you have a large number of records. This will just slow things down unnecessarily. Instead, use WHERE. In the SQL below, we're using WHERE to bring back only the matching records from the AddressBook table:
$SQL = "SELECT * FROM AddressBook WHERE email = 'me@me.com' ";
When the following code is run, only the records that have an email field of me@me.com will be returned.
You can specify more fields in your WHERE clause:
$SQL = "SELECT * FROM AddressBook WHERE First_Name = 'Bill' AND Surname = 'Gates'";
In the SQL statement above, we've used the AND operator as well. Only records that have First_Name value of Bill AND a Surname value of Gates will be returned.
You can also use the operators you saw in the variables section:
$SQL = "SELECT * FROM AddressBook WHERE ID >= '10' ";
In this SQL statement, we're specifying that all the records from the AddressBook table should be returned WHERE the ID column is greater than or equal to 10.
Getting the hang of WHERE can really speed up your database access, and is well worth the effort. An awareness of the security issues involved is also a must.

In the next sections, we'll take you through some fuller projects, and explain the code, and the things you need to consider when working on bigger projects like this. First up is a username and password system.

Comments