Create A Christmas Wish List With PHP (For Beginners)

Advertisement

Editor’s note

Please notice that this article doesn’t provide production code to use in your live websites. Make sure to read the full article and study more on PHP security before using the code listed in this article.

’Tis the season to be jolly, and how much jollier could we make it than with a helpful Christmas wish list crafted for your family to ensure that you get maximum presentage this holiday? In this article, we will focus on creating a very simple system that allows you to add gift ideas to a Web page, and for your family (or whoever) to view the list.

Please notice that this article was written for beginners who already grasp HTML and CSS, know a bit of PHP and have seen phpMyAdmin before. I will not go into best practices, safety and all the rest of it; let’s just have fun with this one!

Preparing Our Resources

Before creating the website, let’s see the finished product:

screenshot
The finished Christmas wish list.

The result will contain three pages: the list itself, a log-in page and an admin page. We will also have a style sheet, two scripts (for logging people in and saving items) and some images. You can view an online demo of the code to see the end result.

Below is a list of the resources I have used. Go ahead and grab them, but feel free to use your own! Please note the licence on each; some are free for commercial use, but some are free only for personal use.

  • I based the colors on the “’Tis the Season” color palette from COLOURlovers.
  • The Santa icon in the logo is from the “Santa” icon set, downloaded from Iconfinder.
  • The Christmas style font is called “Mountains of Christmas” and was obtained from Google Web Fonts
  • I created the background pattern by playing around with the background maker on BG Patterns.

File Structure

We know that we’ll need three pages (list, log-in and admin), but let’s look at the full list of files that we will need. Feel free to create these as empty files in advance.

  • index.php
  • login.php
  • admin.php
  • header.php
  • footer.php
  • config.php
  • script-login.php
  • script-additem.php
  • images
  • uploads

The first three files should be self-explanatory; they will handle the content of our three main pages. We will also use a separate header and footer file; the purpose of these files is to minimize duplicate code.

For all three of our pages, we’ll need to create the code for the header section (i.e. the Santa icon and the title, plus the actual HTML head section and so on). Instead of writing the code three times (which would make the code hard to update), we will write it out once and include the code in all three files.

We’ll also include the config.php file in each file. This sets up the database connection and handles a few other things that are needed for every page.

The next two files in the list are scripts that handle logging in and adding new items to the list.

Finally, we have two directories. The images directory will contain the images we need for our website’s framework. The uploads directory will contain all of the images for our uploaded items.

A Word Of Warning

Note that this is meant as a beginner’s exercise. The code you see here will give you the intended result, but a lot of it is not safe for production websites. It lacks a lot of safeguards, such as data validation, salts for passwords (for better security), htaccess rules and so on. The goal of this article is to let beginners forget about all of these things and just concentrate on building something nice.

Neither does this article promote best practices. You may find yourself adopting different methods later on, or I may write in another article that we shouldn’t do something you see here. The article is intended as a fun little example for beginners to spice up their boring theory sessions. I believe that the best way to learn is through increasingly difficult examples.

That said, I encourage you to try all of this out and play around with it at home or on your servers. If you put this on a live server, I recommend using an account that has only this website on it (or only test websites). I also recommend using passwords for user accounts that are not the same as your other passwords.

Creating Our Framework

Our mini-framework consists of the header and the basic structure of the page. Let’s see what this would look like in HTML:

<!DOCTYPE html>
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

      <title>My Christmas List</title>

      <!-- Style sheets -->
      <link href='style.css' rel='stylesheet' type='text/css'>

   </head>
   <body>

      <div id="site_header">
         <img id="site_logo" src='images/santa_logo.png' alt='The Santa Logo' >
         <h1>Daniel Pataki's Christmas List</h1>
         <div class="clear"></div>
      </div>

      <div id="site_container">

      </div>

   </body>
</html>

The Head

As you know from your studies in HTML, the code above consists of two main sections. The code inside the <head> section is not visible to the user. The <head> contains meta information (such as keywords and a description of the website), the title, the style sheets used, the JavaScript files needed and so on.

Your Web page must have the <head> element and at the very least must contain a <title> element. The content of the <title> tag will be seen in the browser tab. In our case, we have also tied a style sheet to our website using a <link> element.

The Body

Your Web page must also contain a <body> element, even an empty one. Of course, nothing much would happen if we left it empty, so I’ve added the header of our website and the main content area.

The header of our website will be seen on all pages. This is the section with the Santa logo and the title “Daniel Pataki’s Christmas List.”

I’ve used a regular image tag to set the image, and a top-level heading to create the title. I’ve also used an empty <div> element, giving it a class of clear. The reason for this has to do with positioning (more on this when we get to the CSS).

Once we’re done with the header, we need a big white container to hold our list. This is done with a simple <div>, which we’ve given a class of site_container.

Finally, we close the <body> and <html> tags, and we make sure our code is valid by running it through the HTML Validator. Don’t worry yet if your code is invalid. Try to fix what you can, but don’t worry if something isn’t working. Your goal now is to get things working; you’ll have plenty of time later to worry about best practices.

Splitting Up

The code we’ve just written will be common to all of our pages. The only thing we’ll change is the content inside the site_container div (which is currently empty). To avoid having to add this same code three times, it is a good practice to separate it into two files: a header and footer file.

Everything common to the top part of each page should go in header.php, and everything common to the bottom part should go in footer.php.

Copy and paste everything up to and including <div id='site_container'> into the header.php file. Copy and paste everything else into the footer.php file.

If you load the website right now, you should see an empty page with no content. This is because index.php is being shown but is currently empty, so we need to pull in the header and footer code. Use the following code to make that happen.

<?php include('header.php'); ?>

<?php include('footer.php') ?>

If you load the Web page now, you should see the code in place. It will be awfully ugly, but we’ll change all that in a jiffy.

Adding Some Style

Now that our structure is in place, it’s time to make it look nice. Achieving what you see in the finished product is actually very easy, so let’s jump right in.

Resetting styles
The first thing to do with many websites is to reset all browser-specific properties. Each browser handles headings, tables, inputs and other elements slightly differently, so let’s reset all of these styles, which will make the page ugly but at least uniform on every platform.

body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td {
   margin:0;
   padding:0;
}

table {
   border-collapse:collapse;
   border-spacing:0;
}

fieldset,img {
   border:0;
}

address,caption,cite,code,dfn,em,strong,th,var {
   font-style:normal;
   font-weight:normal;
}

ol,ul {
   list-style:none;
}

caption,th {
   text-align:left;
}

h1,h2,h3,h4,h5,h6 {
   font-size:100%;
   font-weight:normal;
}

q:before,q:after {
   content:'';
}

abbr,acronym {
   border:0;
}

This is a commonly used reset called YUI 2 Reset CSS. You can copy and paste it from here, and you can also find it on the Yahoo Developer Network.

Overall structure
The overall structure consists of the body, the header section and the website container. Let’s concentrate on putting these in the right places and adding the proper background to our website.

html, body {
   background:url('images/bg.png');
   color:#555;
   font-size:14px;
}

#site_header {
   width:740px;
   margin:0 auto;
}

#site_header h1 {
   font-family: Mountains of Christmas;
   font-size:42px;
   letter-spacing:1px;
   color:#fff;
   position:relative;
   top:60px;
   font-weight:700;
   text-shadow:-1px -1px 1px #3a5e40
}

#site_logo {
   float:left;
   position:relative;
   top:36px;
   left:-24px;
}

#site_container {
   background:#fff;
   width:700px;
   padding:42px 20px;
   margin:0 auto;
   border:3px solid #7b9971;
}

This should be pretty easy to follow. Let me expand on a few points of interest.

Embedding fonts
It’s a good bet that the Mountains of Christmas font is not installed on your or your visitors’ computers, but we can still use it through the magic of CSS3 and with help from Google Web Fonts. Go to Google’s website now, choose any font, and follow the instructions to use it.

Before referring to a font in your CSS file, you need to embed it with the following code, which goes in the <head> section of your website:

<link href='http://fonts.googleapis.com/css?family=Mountains+of+Christmas:700' rel='stylesheet' type='text/css'>

Clearing elements
If you load the website with this code, the header will seem to collapse into the content area, because the image inside the header is floated left. Whenever you float an element inside another element, you need to make sure the floats are cleared to prevent the content from collapsing like this.

While this behavior might seem odd, it has a logic. Have a look at A List Apart for a good article on floats. Right now, all we need to do is create an empty element at the bottom of the parent element, which will clear the floats above it. To accomplish this, paste the following into the CSS sheet:

.clear {
   clear:both;
}

Now that we’re done with our little framework, let’s add some content to the website!

Creating The Database

Before moving on, you’ll need to put a database structure in place. If you are using your localhost to follow along, you should be able to get to your database through one of the following:

  • http://localhost/phpMyAdmin
  • http://localhost/phpMyAdmin
  • http://localhost:8888/phpMyAdmin
  • http://localhost:8888/phpMyAdmin

The usual problem with accessing phpMyAdmin is that it needs to be capitalized in some installations but not in others. If you use a port number after localhost, make sure to add it.

Once you’re there, find the “Create a new database” field and type christmaslist to create the database. Then, create two new tables with the following structure:

screenshot
Creating the user table.

screenshot
Creating the item table.

Go to the “Privileges” tab in phpMyAdmin to set up a user who can access the database that you just created. Later on, you will need the name of the database, the user name and the password to access it.

Once that’s done, manually create a user in the database. Make sure to select the SHA1 function next to the password field.

screenshot
Encoding a field with SHA1.

Connecting to the Database

While you now have a database, you will need to actually connect to it before you are able to use it. Whenever you need data from the database, you will need to connect to it, retrieve the data and then close the connection.

Just like with the header section of the website, you will be using this database so often that writing the code once and including it on every page would be best.

Let’s go into config.php and add the following code.

<php
$db = new mysqli("localhost", "db_username", "db_password", "db_name");
?>

This code connects to your database and makes it possible for you to read the data and write to the database. But you might have noticed that the config.php file is not referred to anywhere on our page. Let’s fix that by including it in the header.php file, which is included on every page.

include("config.php");

Our code now opens a connection to the database. Because it is included in the header file, we will be able to grab data on any page that we are on. Once we’ve done that, we’ll need to close the connection. We can do this in the footer.php file, because that is the last thing on the page and we won’t need to perform any database actions after the <html> closing tag. So, paste the following line there:

<?php $db->close(); ?>

Logging In

We want to let only people who we know in on this project, so let’s protect it with a log-in function. A user will enter their name and password, and our script will check whether a user with these credentials is in the database.

HTML and CSS

Go to the login.php file, and create a log-in form like this:

<form method="post" action="script-login.php">

   <h1>Please log in</h1>

   <label for="username">username</label>
   <input type="text" name="username" id="username">
   <br>
   <label for="password">password</label>
   <input type="password" name="password" id="password">
   <br>

   <input type="submit" value="Log In" class='submit'>

</form>

While the form is usable as is, it looks a bit funny, so let’s add some CSS style to spruce it up:

h1 {
   font-family:georgia;
   margin:0 0 12px 0;
   font-size:32px;
   color:#7b9971;
}

input, textarea {
   border:1px solid #7b9971;
   background:#e9fce3;
   padding:5px;
   font-size:16px;
   width:300px;
   margin:0 0 6px 0;
   display:block;
}
textarea {
   height:140px;
}
label {
   font-size:13px;
   margin:0 0 4px 0;
}
input.submit {
   border:0px;
   width:auto;
   color:#fff;
   background:#941f1f;
   font-size:12px;
   padding:5px 12px;
   border:1px solid #7d1515;
}

The data from this form will be submitted using the POST method, which is safer for most data transfers. The difference between the POST and GET methods is mainly that data transmitted via the GET method can be seen in the URL, while data passed with the POST method cannot.

The PHP Script

Once the user has submitted the form, the data will be transferred to script-login.php, so let’s go there now and deal with the data:

include("config.php");

$username = $_POST['username'];
$password = sha1($_POST['password']);

$query = "SELECT ID FROM user WHERE username = '$username' AND password = '$password'";

if ($result = $db->query($query)) {
   while ($user = $result->fetch_object()) {
      $_SESSION['user'] = $user->ID;
      header("Location: index.php");
   }
}
else {
   header("Location: login.php");
}

$db->close();

Remember how we included config.php file in every file? What gives? Why are we including it here as well? Since this is a script, we do not need our usual header section. We just need to process some data and move the user along.

Our config.php file is included in header.php, so if there’s no header.php file here, then there won’t be any config.php code either. So, we need pull in the config.php file separately and also close the database connection in the last line.

Let’s break the code down. First, we store the user name and password in two separate variables. This is not necessary but adds clarity. You might have noticed the sha1() function being used. This is an algorithm that converts plain-text passwords into a string that is almost impossible to decipher.

When a user registers and gives “superawesome” as their password, this is immediately run through the SHA1 algorithm. Instead of storing the plain-text password, we store the result of the algorithm, which is 0ac45a31f4576e727746e0ab26b916df0e0eb890.

When the user logs in, we don’t actually know (or care) what the plain-text password is. We simply take the value that they’ve entered and use the SHA1 algorithm to see whether the SHA1-encoded password stored for the user’s name is the same as this SHA1-encoded string. If it is, then the user is allowed to log in.

The power of this method is that an intruder would not be able to see the user’s password even if they had direct access to the database. Sure, they’d see that the user’s SHA1-encoded password is 0ac45a31f4576e727746e0ab26b916df0e0eb890, but they wouldn’t be able to do a whole lot with that.

Moving on, we write our query and store it in a variable. We check that the ID of the person whose user name has been entered is correct and that the password belonging to that user name is the given password. If both criteria are met at once, we can log the user in.

The query is executed by the following code: $result = $db->query($query). I have encased this in an if statement. If we get a result, we will be able to use it through the $result variable. If we don’t get a result, then the whole expression will be false, and so we wouldn’t let the user log in and would just redirect them to the login.php page.

If we do get a result, then we know that the user has already entered a correct user name and password combination, so we’d let them log in and redirect them to the main list.

We do this by looping over the retrieved users (there will be only one) and storing the user’s data in the $user variable. We then create a session variable using the user’s ID and check for its presence on every page. If it is not present, then we’d know that the user is not logged in and so we would redirect them to login.php. Finally, we would direct the user to the main list.

To make this work, we need to do one more thing. Session variables can be used to pass data from one page to another, but we need to start a session first. As long as a session has been started on a page, we can pass values to it from other pages. This sounds like something we’ll need on all pages, so let’s add it to our config.php file, right at the top.

session_start();

If you go now to the log-in form and enter a valid user name and password combination, the following should happen. You should be directed to the script that checks for a user with the given credentials. If it finds them, it creates $_SESSION['user'] with the value of the user’s ID. It will then direct you to main list (index.php), where the session variable should be available.

You can check this by going to the index.php file and echoing the contents of $_SESSION['user'].

Redirecting Logged-Out Users

Because we’ve set the session variable for all logged-in users, we can redirect anyone who doesn’t have this variable set to the log-in page. By the way, these session variables will remain usable until the user closes the browser.

We will also need redirection on all pages, so let’s add it to the config.php file at the very bottom, like so:

if( (!isset($_SESSION['user']) OR empty($_SESSION['user'])) AND $_SERVER['REQUEST_URI'] != '/login.php') {
   header("Location: login.php");
}

If the session variable is not set or is empty (and the user is not on the log-in page), then we redirect the user to the log-in page. The last bit is needed because we want to allow everyone on to the log-in page — even people without a session variable, of course.

The Admin Page

Our admin.php file will consist of a simple form to add new items. By now you should not be able to visit this page if you are merely logged in. Open up the admin.php file and let’s add our form.

<form method="post" action="script-additem.php" enctype="multipart/form-data">
   <h1>Add an Item</h1>

   <label for="name">name</label>
   <input id="name" name="name" type="text">

   <label for="description">description</label>
   <textarea id="description" name="description"></textarea>

   <label for="link">link</label>
   <input id="link" name="link" type="text">

   <label for="price">price</label>
   <input id="price" name="price" type="text">

   <label for="image">image</label>
   <input type="file" name="file" id="file">

   <input type="submit" class="submit" value="add item">

</form>

Our existing styles should make this look OK, so we can go straight to the points of interest. This is all pretty standard unless you’ve never looked at file uploads.

To enable file uploads, we need to add two things to the form: an input field of the type file, and the encoding type (in this case, multipart/form-data). If you want to be able to upload files, you will always have to specify the enctype parameter.

We blew through that pretty quickly, so let’s continue with the script that does the heavy lifting, script-additem.php:

include("functions.php");

$target_path = $_SERVER['DOCUMENT_ROOT']."/uploads/";
$target_path = $target_path . basename( $_FILES['file']['name']);
$file_location = "uploads/".$_FILES['file']['name'];
move_uploaded_file($_FILES['file']['tmp_name'], $target_path);

$query = "INSERT INTO item (name, description, price, link, image) VALUES ('$_POST[name]', '$_POST[description]', '$_POST[price]', '$_POST[link]', '$file_location')";
$result = $db->query($query);

$db->close();
header("Location: admin.php?uploaded=true");

The first thing to take care of is file uploading. We created the uploads directory for this purpose, so let’s pass the path to it to our script.

Take care because this may not be the code you need to use. If you are using a subdirectory, you may need to use $_SERVER['DOCUMENT_ROOT']."/subdirectory/uploads/" or something like it. If in doubt, print out the contents of the $_SERVER variable and try to find your path from there.

Once our target directory path is set, we need to add the file name to it to make it a complete path. Our target path now contains the absolute path to the file, something like /home/username/public_html/christmaslist/uploads/myuploadedimage.jpg.

To make sure we have the URL that we’ll add to the database, we store it in the $file_location variable. This is different from $target_path because it doesn’t have the /home/username/public_html/christmaslist part of the URL.

We use the move_uploaded_file($_FILES['file']['tmp_name'], $target_path); line to upload the files. The move_uploaded_file() function takes two arguments: the temporary location of the file and the new location that we want to put it in.

At this point, our image has been uploaded. It’s time to add the data of the item to the database. The data we need contains the URL of the image as well, which is why we had to upload the image before adding the item to the database.

We store our query inside the $query variable, execute it, close the connection and redirect the user back to admin.php. I added the ?uploaded=true query string to the URL; we will use this to show the user a confirmation message. Head back to the admin.php file and paste the following in it just under the include:

<?php
   if(isset($_GET['uploaded']) AND $_GET['uploaded'] == "true") {
      echo "
<div class="success">The item has been added</div>
";
   }
?>

This is the GET method, and as you can see, the data is visible in the URL. Using this here is OK, of course; we’re just using it to detect whether an item has been successfully added. To make it prettier, we’ll style this div like so:

.success {
   padding:5px 12px;
   background:#7b9971;
   border:#7b9971 - #222 solid 1px;
   color:#fff;
   margin:0 0 12px 0;
}

The form should now be ready to use, so go ahead and add three to four test items, complete with images and all.

Listing Our Gift Ideas

The only thing left to do is list the items in the database for our visitors. Let’s head over to the index.php file and add the following code to do this:

<?php include('header.php'); ?>

<table class='item_list'>

<?php
   $query = "SELECT * FROM item";
   $result = $db->query($query);

   if($result) : while ($item = $result->fetch_object()):
?>

   <tr class='item'>
      <td class='item_image'>
         <img width="150" src='<?php echo $item->image ?>'>
      </td>
      <td class='item_content'>
         <h2 class='item_name'><?php echo $item->name ?></h2>
         <div class='item_description'><?php echo nl2br($item->description) ?></div>
      </td>
      <td class='item_price'>
         <h3>
            <a href='<?php echo $item->link ?>'>
               <span class='price'>$<?php echo number_format($item->price) ?></span>
            </a>
         </h3>
         <a class='text' href='<?php echo $item->link ?>'>Buy This</a>
      </td>
   </tr>

<?php endwhile; endif; ?>
</table>

<?php include('footer.php') ?>

The frame of this page is created by including the header at the top and the footer at the bottom. This includes pulling in our config.php file and closing our connection, so we can forget about that here.

We’ll pull in all of the items and show them using a table. We use while ($item = $result->fetch_object()) to iterate through all of the results. We want the exact same structure for each results: a three-column row. The first column will show the image, the second will show the name and a description, and the third will show the price and a purchase link.

The two functions worth mentioning here are nl2br() and number_format(). The former converts line breaks into <br> tags. The latter converts numbers into a nicely formatted string, adding in thousand separators as needed.

To make this all nice and proper, let’s add some CSS:

.item td {
   border-bottom:1px solid #ccc;
   padding:12px 0;
}

.item_image img {
   margin:0 16px 0 0;
}

.item_price h3 {
   margin:0px;
}

.item_price a {
   font-weight:700;
   font-family:Mountains of Christmas;
   color:#941f1f;
   text-decoration:none;
}

.item_price a:hover {
   color:#941f1f + #252525;
}

.item_price .price {
   font-size:52px;
}

.item_price .text {
   position:relative;
   top:-12px;
}

.item_content {
   padding:0 42px 0 0;
   width:340px;
}

.item_content h2 {
   font-size:24px;
   font-family:georgia;
}

A Note On SQL Security

While this article as a whole should not be used in production environments (it is only a basic example to aid in your studies) commenters have pointed out – correctly – that some level of SQL security should be added.

A great (and simple) method of protecting against any naughtiness (like SQL injection) is to escape strings.

Instead of using the data from variables directly we’ll first run them through the mysqli_real_escape_string() function which will take care of the work for us. Let’s take a look at an example which we could use to search our items.

$term = $_GET['term']; // This is the term the user searched for
$term = $db->real_escape_string($term);
$query = "SELECT * FROM item WHERE name LIKE '%$term%' ";
$result = $db->query($query);

You can use this method to make sure all your SQL code is better suited to a working environment. A more elaborate (but even safer) way to go is preparing your statements. Preparing your statements using mysqli_prepare() is a more effective way of ensuring proper safety.

In addition to all this you should be sanitizing and validating any data that your application receives. This involves making sure that when you’re looking for a date you receive the correct format (or convert into it), if a value needs to be between 1-10 it indeed is, and so on.

Creating a secure environment is a mammoth task which you won’t be able to lear all at once. However there are many small things (like escaping string) you can do which will make your site much more secure.

The Way Forward

If you’ve been following along, you should now have a rudimentary but functioning website that lists your gift ideas. It is far from complete, but you can already expand on the knowledge and practice you’ve gained here by adding a user-registration form to admin.php that enables you to add users from the back end.

If you use a couple of other readily available techniques, you can add a whole bunch of other useful features, such as:

  • Enabling pagination of gift items (if you have a ton of requests);
  • Enabling public access to the list but not the admin section;
  • Enabling public access to the list but restricting access to the admin section so that only you can use it (i.e. not even other logged-in users);
  • Enabling themes for other holidays;
  • Allowing logged-in users to reserve gifts or indicate that they have bought one;
  • Enabling different currencies;
  • So much more.

Closing Thoughts

I would like to stress again that this was not an exercise in best practices, safety, security or flawless coding standards. It was an exercise to get beginners started, without bogging them down with a lot of supplementary information; a fun exercise to give you something tangible quite quickly. You can work on making it better and more secure and feature-rich on your own.

If you have any questions or get stuck, do drop a comment. I or someone in the community will surely help out as soon as we can! Don’t forget to check out the online demo.

Happy holidays, and I hope you get all the items on your list!

↑ Back to top

  1. 1

    Really great article! Any chance you have a link to suggestions listed at the end? More specifically the one that allows logged-in users to reserve gifts?

    0
    • 2

      Hi Chris!

      I’m sure there are tutorials on this, but you do have to figure out things for yourself to become good at developing stuff! I’m sure there will be a tutorial around soon covering that though ;)

      0
  2. 3

    Nice idea for the simple php app tutorial, but consider sanitizing the params passed to the database. IMO it is important to teach newbies to do these things from the very beginning.

    “SELECT ID FROM user WHERE username = ‘$username’ AND password = ‘$password’”;

    is not an option for a production app. Always sanitize your db input by either mysql_real_escape or by using prepared statements.

    0
  3. 4

    Good article for beginners, but have you ever heard of SQL injection vulnerability?

    0
    • 5

      I don’t think beginners need to concern themselves with SQL injection attacks. The point here is to start to learn something, not to learn everything at once. When someone understands SQL at all, then teach them about the problems, not before

      0
      • 6

        The problem is that if they just copy-paste this code to their website and get their database erased in a few days, they won’t be happy at all. Also, I think they shouldn’t get used to bad practices but I agree with you that some concepts just not really fit in to an article for beginners.

        A compromise would be to provide the code examples the correct way without detailed explanation and add an external link to an article about what that all is about for the readers who are interested.

        0
        • 7

          I think teaching people to do things is very complicated, doubly so over the internet. If I were teaching a university class I would take a very different approach.

          The truth is that no matter how much security advice you give people beginners won’t be able to implement it effectively so they could get their db erased a day later anyway.

          Someone who will be a great developer one day will keep at it and learn how to do better.

          I agree on your external link idea but I would need to link in about 100 articles because if I DO cover SQL injection some people will complain about not enough sanitization. if I link that in people will complain about mysqli. If I link alternatives in I will be in a situation where I’m linking to C# articles and NoSQL solutions like MongoDB in an article aimed at absolute beginners.

          0
          • 8

            As said: just some hotfixes to the article would be fine.
            You don’t need to explain what an SQL injection is, you just have to put a comment like

            //checking that user doesn’t post evil data :)

            Just a hotfix :)

            0
  4. 9

    Very nice, Thank you.. can you please add some basic security (like an above poster mentioned) so new people know its not ok to just accept those inputs without checking them first.

    0
  5. 10

    Nice tutorial, but I wouldn’t suggest it to anyone as I too see security issues everywhere in here.
    For instance:
    – session save path?
    – session name?
    – SQL injections? (You’re using MySQLi! Use parameter placeholders please!)
    – Checking extension/mime of uploaded files?

    For anyone reading: this is a tutorial, yeah, and a simple one, but “don’t do this at home!”
    This is exactly the kind of code that forces me to spend hours of “rm -rf some_old_unsecure_stuff”

    0
  6. 11

    For anyone commenting on the security in this article.

    Have you ever seen a seventh grader calculate speed by using integrals to get to the area under the curve? No, they use distance/time. The 7th graders are completely wrong, just like when they use 10m/s as acceleration when an object is dropped.

    You can not teach programming by going over everything in one article. You omit, you tell small lies and when the person is ready he/she can move on to more complex things.

    0
    • 12

      And then I have to fix it? No, thanks. First you learn to use the tools, then I’ll let you write your hello world. And please on your own SVN branch, and then I’ll *maybe* review it and merge it to master if you did a good job.
      Programming is not like typing random characters on the keyboard, there’s money and time going through it, and this thing on a company server that isn’t running suPHP or that is running APC or generally isn’t hardened “because it has only our stuff” would be a seriously great problem.
      We’re not banging rocks together out here.

      - Please note that there isn’t that much need for complex, giant code pieces to fix this stuff… So (imho) it is pointless to say “newbies don’t need this”.

      0
      • 13

        We can’t all have come out of the womb coding php like Marco… Can we?

        Gotta start somewhere… I’d just hope beginners realize 90% of the stuff they find on the web is a PIECE of the much bigger pie to their education, destiny for better programming, and the puzzle they are trying to solve.

        0
  7. 14

    “Neither does this article promote best practices. You may find yourself adopting different methods later on, or I may write in another article that we shouldn’t do something you see here.”

    This is part of the reason PHP gets such a bad rap – people start learning from tutorials like this, then either have learn properly further down the line or dont learn further and continue to produce insecure, bad code.

    I’m sorry but I don’t really see this article as a good thing, you’d probably be better off spending a little less time on the HTML/CSS side of things and dedicate more time to teaching better PHP practices from the start – separate the two if needs be.

    0
    • 15

      I think I agree with this approach (I mean your opinion) much more than I agree with the security related comments.

      What most people don’t realize is that everybody learns differently. For me this approach is more appealing. I agree that it DOES lead to some people coding in a sub-par manner. However, I believe it also leads to some SUPERB coders.

      If you go with the strict separation you get less sub-par coders because you will get a higher dropoff rate.

      I will definitely write an article in the future which is focused on beginners but has a much more PHP oriented approach.

      Thanks for that critique, I will keep it in mind for a future article.

      0
  8. 16

    Yeah, an update to the security would be great.
    Great article, will give a shot!

    0
  9. 17

    Good stuff just to have some fun and help the super beginners get a quick footing. I think a lot of the people commenting here are either A) Too seasoned to look this far back, and not doing things the “proper” way just irks them, or B) I’d be willing to bet some are just flexing their programmer’s ego a bit.

    I think assuming that people will take this as serious programming and build from it, building the wrong way, is a bit too much of a stretch. Anyone who can read and who cares about doing things the right way will take the author’s disclaimer to heart. If not, odds are they’re looking for the easy route. If that’s the case, you can’t really stop them. This article isn’t ending the world.

    0
    • 18

      I would have to raise an argument here that, ironically, your suggestion that some of the commenters here are “Too seasoned to look this far back…” seems to reflect yourself in that, when I first started learning PHP, alot of my first builds were copy-and-pastes from tutorials like this.

      The other commenters are not saying that people will “…take this as serious programming and build from it, building the wrong way…”, but they will take it as programming (even if not serious) and they may reuse the same core in any later development they will do. I cannot tell you the number of times I have been brought in to fix a system where poorly written, grandfathered, never-revisited and revised code, which is obviously from an early iteration of an unrelated system has been carried over and caused the lion’s share of the headaches I have had to solve.

      Good coders write, great coders steal, greater coders reuse.

      Helping people shape the building blocks for their later developments with an awareness of SQL injection, isolating markup from logic, etc. is never wasted time. Wasted time is what is spent trying to correct bad habits down the track.

      0
      • 19

        “The other commenters are not saying that people will ‘…take this as serious programming and build from it, building the wrong way…’, but they will take it as programming (even if not serious) and they may reuse the same core in any later development they will do”

        Potato-Potahto…

        0
  10. 20

    I don’t think it’s simpler at all to neglect security. At the very least teach users to use mysql_real_escape_string. Or just use PDO from the start. Just because you started by learning the mysql_* functions, doesn’t mean it’s the easiest for new users.

    All this is kind of moot however, when you publish an article that’s clearly weeks too late to be useful ;)

    0
  11. 21

    Daniel Pataki: Nice article, I agree with you. In my first programs, I didn’t protect the code against SQL Injection. This is not a security article, it’s just… how to create a xmas wish list.

    0
  12. 22

    Security should never be an afterthought.

    If you want to teach someone something teach them the right way the first time.

    Smashing Magazine lost some cred with me on this article.

    0
  13. 23

    If you’re a pro, you’ll code the proper way for yourself and your clients. If you’re a beginner, who cares? This isn’t life or death surgery, people. Maybe a Christmas list database gets deleted… the world is not going to end. If fact, it would be wonderful if a hacker did attempt an SQL injection. The beginner would learn from the mistake, do some research, and his or her next script would be that much more secure.

    0
  14. 24

    Hi Daniel,

    Thanks for the tutorial, but for a beginner it is actually a little difficult to follow along. You don’t mention php open / close tags in a lot of places, whether the stylesheet for the login should be inline or added to the main style.css, and the details of including the config / header / footer files into the other pages. Following the tutorial as closely as possible has left me quite confused – are you able to include the source files in order to clear a few things up?

    0
  15. 25

    TYPO
    Creating a secure environment is a mammoth task which you won’t be able to “lear” all at once.

    lear = learn

    0
  16. 26

    I feel that beginners are not being given enough credit. Yes, they are starting out in the coding world but im sure that they are not complete idiots.

    Teaching someone coding without the right tools to protect themselves in the name of simplicity is silly and if the hackers get a hold of their sites things will get very complicated.

    0
  17. 27

    Hey, can you attach the whole files? i tried to make it but unfortunately it doesn’t work..
    please if you can attach the work files :), thanks in advance

    0
  18. 28

    When you are talking about writing the script-additem.php portion you make reference to an include called functions.php file. I don’t see any other reference to this in the article so I was wondering if that was a type or what was going on with that?

    –I’m assuming it should be config.php?

    0
    • 29

      I’m with you Roger. Everybody is so busy beating up Daniel for a very helpful article that nobody noticed that there’s no functions.php mentioned anywhere.

      /*
      Daniel, I’m a newbie. I’m still working through learning this stuff. Thanks for not making it depressingly overwhelming for me.
      */ <=hope I did that right.. :-)

      0
  19. 30

    In all future articles please consider using prepared statements or at least escape the data. When I was a beginner the very last thing I liked was being treated like a baby. Beginners want to learn the right way the first time, not wasting time learning bad ways.

    0
  20. 31

    Thanks for the tut, Daniel! I for one appreciated it. I am a true beginner, attempting to learn HTML, CSS, PHP, and MySQL all at the same time.. probably not the best way, but it is all starting to come together in my head.

    Anywho, being a beginner I would like to say that I am interested in learning best practices. As a previous poster mentioned, simply including it into the shown code and then in explanation referencing an outside source would have been perfect. I could still play around with the code on my local machine to help put it all together in my head, and then research the referenced links at a later time to fully understand the purpose and responsibility in utilizing the referenced material.

    I know that is a lot more work for you, the author, but it makes your tut more complete, correct and your readers more informed and knowledgeable.

    With all of that said, please please continue writing these tuts as this is just what I need!!

    0
  21. 32

    i agree, source files would be v. helpful.

    the following code brings up an error:
    <?php
    if(isset($_GET['uploaded']) AND $_GET['uploaded'] == "true") {
    echo "
    The item has been added
    “;
    }
    ?>

    the double quotes around “success” stops the string short – one way to fix this is by using single quotes: ‘success’

    0
    • 33

      ha, oops. (good security, smashing magazine!). it took out this part of the code:

      < div class=”success” > The item has been added < /div >

      0
  22. 34

    Why is everyone complaining about the security of the code when the author plainly states from the beginning not to use it on a production server. If all of you that are complaining are such experts why don’t you write a damn article instead of blasting the author when he clearly states his intentions of this article from the start. I think the technique of keeping things simple to learn and add complexity later is a great idea so you understand how the bigger picture works.

    0
  23. 35

    Quite good article.

    0
  24. 36

    Hello. Does anyone know what is all about this cookie acceptation thing? Is it safe?

    Thanks for answer

    0
  25. 37

    1 online demo does not work!

    2 files are not supplyed with this tut article!

    3 secuirty should be always mentioned from the start ie sanitation prepered statments not needed for beginners but real escape string is a must even for beginners! end of :) other than all that its ok artikle but please supply a zipped archive with all files used including images and an sql file for the database this is alot better than working from complete scratch!

    i am not complaining im just trying to help those beginners out there!

    0

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top