Diving into PHP and SQL

May 03, 2019

php logo

Ah yes, the ever-so controversial PHP. When I was initially getting into web development PHP was one of the first things I considered learning, though this was soon abandoned after somehow developing — through sites like reddit no doubt — a perception of PHP as dated and, well, bad.

However, those were the perceptions of a naive programmer — quickly rushing to judgment on a technology one has no experience with.

And while PHP certainly has its share of memes and haters, a more accurate interpretation of the communities perception towards the language would be thus:

PHP has a bad reputation because a lot of inexperienced programmers built crappy things with it. However, if you follow best-practice, it is an extremely powerful tool for building websites.

Anyway, let's get to some code and uncover the enigma that is PHP!

Configuring PHP

Here's the tutorial I used to configure PHP on Mojave OS: Setting up a local web server on macOS 10.14 “Mojave”

I decided to use my Mac for this since Apple computers come with PHP already installed, the trick is you need to activate it.

This means manipulating the /etc/apache2/httpd.conf file, which if done improperly can destroy your machine, so make sure you follow the directions in tutorial linked above to the #!

The Project

Here is the github repo to what we are building: php-mysql repo.

After going through the tutorial linked above, you should know where your PHP files are being served from on your machine. It is likely from a directory located at /Users/<username>/Sites/ and can be accessed in your browser via http://localhost/~<username>.

We're going to make a little form that sign's a user up to a MySQL database. Create a directory php-form in your Sites folder and add the following files:

/php-form
    header.php
    form.php
    sql.php

form.php

Open form.php and add the following code:

<?php include 'header.php' ?>
<body>
    <div style="margin-top: 20px;" class="container">
        <header class="jumbotron">
            <h1 class="display-3">Sign up</h1>
            <p class="lead">Join us as we explore PHP and SQL!</p>
        </header>
        <form action="sql.php" method="post">
            <p>
                Name: <input name="name" type="text">
            </p>
            <p>
                Password: <input name="password" type="password">
            </p>
            <input class="btn btn-primary" type="submit" value="Sign Up">
        </form>
    </div>
</body>
</html>

Everything inside <?php ?> is PHP code, so you may notice only one line of PHP here used to import our header file. PHP and HTML work really well together. For example, this is a .php file, yet it's essentially all HTML. And though we will soon see that PHP has much more powerful functionality than allowing HTML in our files, this just demonstrates that PHP is a language for websites.

Now let's open our header.php and see another another interesting way in which PHP interacts with HTML.

header.php

Add the following code to header.php:

<?php
echo '
<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8"> 
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
    <meta http-equiv="X-UA-Compatible" content="ie=edge"> 
    <title>Sign Up</title> 
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
'
?>

After you save, refresh the browser and you should see a simple sign-up page with some Bootstrap stylings.

As I mentioned above, everything inside of <?php ... ?> is PHP code. In this example, we are using PHP's echo function to generate HTML.

I didn't add a footer.php, so that I suggest you try adding it now.

sql.php

Before getting started, you will need a MySQL db to connect to. If you are not familiar with MySQL/don't have it installed, I suggest you work through their documentation before moving forward: Getting Started with MySQL

Okay, now we are really going to get a taste of what PHP can do. I am going to show the entire file first, then we will break it down:

<?php include 'header.php' ?>

<body>
    <div style="margin-top: 20px;" class="container">
        <header class="jumbotron">
            <h1 class="display-3">Registration</h1>
            <p class="lead">Join us as we explore PHP and SQL!</p>
        </header>
        <?php 
            // Connect to DB
            $config = parse_ini_file("../../../private/config.ini");
            $host = "127.0.0.1";
            $mysqli = mysqli_connect($host, $config["username"], $config["password"], "menagerie");
            if (mysqli_connect_errno($mysqli)) {
                echo "Failed to connect to MySQL: " . mysql_connect_errno();
            }
            // Check if user table exists
            // TODO find the right way to do this
            $query = "SELECT * FROM user";
            $res = mysqli_query($mysqli, $query);
            // Create user table  if it doesn't exist
            if (!$res) {    
                $res = mysqli_query(
                    $mysqli, 
                    "CREATE TABLE user (name VARCHAR(20), password VARCHAR(60))"
                );
            }
            // User sign up
            if ($_POST) {   
                while (TRUE) {        
                    $name = htmlspecialchars($_POST["name"]);
                    $password = password_hash(
                        htmlspecialchars($_POST["password"]),
                        PASSWORD_BCRYPT
                    );  
                    // Make sure form fields aren't empty
                    if (strlen($name)<1 || strlen($password)<1) {
                        echo "Missing form data <br/>";        
                        break;
                    }
                    // Check if user exists already
                    $name_query = "SELECT * FROM user WHERE name='$name';";
                    $name_res = mysqli_query($mysqli, $name_query);
                    if ($name_res->num_rows > 0) {
                        echo "That user exists <br/>";      
                        break;  
                    } else {
                        // Sign the user up            
                        $new_query = "INSERT INTO user (name, password) VALUES ('$name', '$password')";
                        $name_res = mysqli_query($mysqli, $new_query);
                        echo  "User Successfully signed up <br/>";
                        break;
                    }      
                } 
            } 
        ?>
        <a href="form.php">Go back</a>
    </div>
</body>
</html>

db logic

$config = parse_ini_file("../../../private/config.ini");
$host = "127.0.0.1";
/*
 mysqli_connect("127.0.0.1", "my_user", "my_password", "my_db"); 
*/
$mysqli = mysqli_connect($host, $config["username"], $config["password"], "menagerie");

In php $ is used to declare variables. Here I am using the built-in function parse_ini_file() to parse a config.ini file and assign it to var $config, though for practice purposes on your local machine you just pass the raw strings.

Let's check to make sure the connection was succesful:

if (mysqli_connect_errno($mysqli)) {
        echo "Failed to connect to MySQL: " . mysql_connect_errno();
    }

mysqli_connect_errno returns the following, from the docs:

An error code value for the last call to mysqli_connect(), if it failed. zero means no error occurred.

Like Python and JavaScript, 0 is falsey in PHP.

Check if user table exists

$query = "SELECT * FROM user";
$res = mysqli_query($mysqli, $query);
// Create user table  if it doesn't exist
if (!$res) {    
    $res = mysqli_query(
        $mysqli, 
        "CREATE TABLE user (name VARCHAR(20), password VARCHAR(60))"
    );
}

So I am almost positive this is not the proper way to do this, but it is a temporary solution to see if our user table exists.

We are using mysqli_query() to pass queries to our db object. If there is not a user table, $res will return a falsely value — if there is no user table, we will create one.

POST Logic

if ($_POST) {   
    while (TRUE) {        
        $name = htmlspecialchars($_POST["name"]);
        $password = password_hash(
            htmlspecialchars($_POST["password"]),
            PASSWORD_BCRYPT
        );  
        // Make sure form fields aren't empty
        if (strlen($name)<1 || strlen($password)<1) {
            echo "Missing form data <br/>";        
            break;
        }
        // Check if user exists already
        $name_query = "SELECT * FROM user WHERE name='$name';";
        $name_res = mysqli_query($mysqli, $name_query);
        if ($name_res->num_rows > 0) {
            echo "That user exists <br/>";      
            break;  
        } else {
            // Sign the user up            
            $new_query = "INSERT INTO user (name, password) VALUES ('$name', '$password')";
            $name_res = mysqli_query($mysqli, $new_query);
            echo  "User Successfully signed up <br/>";
            break;
        }      
    } 
} 

$_POST

First, let's talk a little bit about $_POST. Here is it's description from the PHP documentation:

[ $_POST ] is an associative array of variables passed to the current script via the HTTP POST method when using application/x-www-form-urlencoded or multipart/form-data as the HTTP Content-Type in the request.

You can think of an associative array as a dictionary, object, hash-table, etc. Basically an implementation of key-value mapping. Thus, to get the values from our post, we can pass a key to $_POST (e.g., $_POST["name"]).

Furthermore, $_POST is what is known as a superglobal, which is defined the in the docs as the following:

[ $_POST ] is a 'superglobal', or automatic global, variable. This simply means that it is available in all scopes throughout a script. There is no need to do global $variable; to access it within functions or methods.

Here is a list of the PHP Superglobals: PHP Superglobal list

Once we submit a post request to sql.php, we'll enter our if ($_POST) block, and thus our while (TRUE) loop, where we'll make use of some more powerful built-in PHP functions.

htmlspecialchars()

 $name = htmlspecialchars($_POST["name"]);

Remember how easily we generated HTML using PHP in our header file? Well, it's okay if we are generating HTML, but we don't want the user to generate HTML, which may include malicious scripts via <script> elements. What htmlspecialchars() does is convert things meaningful to HTML, such as >, < symbols, into their character reference, (e.g., & to &amp;).

password_hash()

$password = password_hash(
            htmlspecialchars($_POST["password"]),
            PASSWORD_BCRYPT
        );  

Now that our user input is clean, we'll need to hash our password. And, you guessed it, PHP has a built-in function for that: password_hash(). From the docs:

password_hash() creates a new password hash using a strong one-way hashing algorithm. password_hash() is compatible with crypt(). Therefore, password hashes created by crypt() can be used with password_hash().

We'll use bcrypt for security purposes.

MySQL

After some simple validation to make sure the post values aren't empty, we can add the user to the db. Of course, we don't want to add a user if that user's name is already in the user table, so let's check to make sure that does not happen:

$name_query = "SELECT * FROM user WHERE name='$name';";
$name_res = mysqli_query($mysqli, $name_query);
if ($name_res->num_rows > 0) {
    echo "That user exists <br/>";      
    break;  
} ...

One that about declaring all variables with $ is that it makes string formatting really easy, as you can see from line 1 above.

Inside the if statement above, you'll see $name_res->num_rows. What's happening here is -> accesses the value of num_rows, which is a property of $name_res. If a user exists with the same name as the person currently signing up, the number of rows will be greater than 0, viz., 1. If that's the case, we will break out of our while(TRUE) loop.

Now we can finally sign the user up.

...
} else {
    $new_query = "INSERT INTO user (name, password) VALUES ('$name', '$password')";
    $name_res = mysqli_query($mysqli, $new_query);
    echo  "User Successfully signed up <br/>";
    break;
} 

And that's about as far as I got with my little trip to PHP.

Final Thoughts

PHP seems pretty cool for building websites, namely due to the power if its built-in libraries. The number of things you can do without including an external library is really impressive. Some aspects of the language would take some getting used to, such as the fact that all variables are global. But otherwise, it's I'm glad I didn't buy into the memes and explored PHP.