using Programming;

A Blog about some of the intrinsics related to programming and how one can get the best out of various languages.

Getting Started with PHP: Part 5

Getting started with PHP: Part 5

We last went through some of the object-oriented features of PHP, specifically the basics of classes and function. In particular, we covered the following:

  • What are classes;
  • Basics of instance functions;
  • Basics of static functions;
  • Class variables;
  • Visibility modifiers;
  • Constructors;
  • Constants;
  • Including other PHP files via include, include_once, require and require_once;
  • Interfaces;

Today, we're going to start playing with another feature of PHP: GD.

What is GD?

GD, or "graphics draw", is a set of functions in PHP that allow developers to work with images. Reading, editing, drawing, loading and saving, in particular.

Any time you read an image to display it, GD is doing that work. The only exception is if you stream an image (as data) from one source to another. We'll actually look at that later on in this session.

At it's core, GD is the combination of all image functions in PHP: reading images, saving images, editing images, drawing shapes and so on.

What is an image?

It may seem silly, but before we work with GD we have to first talk about what an "image" is.

We're not going to talk about ways images are "encoded" or "stored", we're strictly going to talk about what make an image...well...an image.

An image at it's most abstract is a visual representation of a graphical scene, but, we don't want to talk about the abstract: we want to talk about something more concrete.

In technology, an image is a sequence of points of various colors that represent a visual picture.

We typically represent colors with either an RGB, HSV, or HSL value, all with potential optional "alpha" components. We're going to look at the first two of these, starting with RGB, because it is perhaps the simplest to understand.

Note: there are other color representation methods, such as CMYK (often used in printing), but we typically deal with the three aforementioned the most in computing. We often use the others in specialized scenarios, but we won't be talking about those scenarios here.

What are Colors?

Let's talk about colors for a moment.

A "color" is just a way of representing the light spectrum: that is, how we "see" things. We can even "measure" colors with sensors that can identify the frequency and brightness of the light.

RGB Colors

At a basic level, we can think of a color as a mixture of red, green, and blue components. In school, you may have learned that red, green and blue are the three "true" primary colors. That is, any color can be created by mixing a different percentage of those three colors. For example, by mixing red and green we can create yellow, by mixing red, green and blue we can create "white". By not using any of the colors we create "black".

In computing systems we represent them in a few different ways, but for our work we'll be using values of 0 through 255 for each color component when we discuss them, in a "tuple" or comma-separated list in the order "red, green, blue": RGB(0, 0, 0) represents black, RGB(255, 255, 255) represents white, RGB(255, 255, 0) represents yellow, as discussed before.

HSV Colors

The next thing to discuss is "HSV" color representation, which is hue, saturation, value. In this representation, colors are represented with three properties:

  • "hue" which is the "frequency" of the light resembled by the color (typically 0 to 360);
  • "saturation" which is how much of the color vs. white is present (typically 0 to 1), a value of 0 means the white/hue balance is entirely on the white side, and a value of 1 means the white/hue balance is entirely on the hue side;
  • "value" which is how much of the color vs. black is present (typically 0 to 1), a value of 0 means the black/hue balance is entirely on the black side, and a value of 1 means the black/hue balance is entirely on the hue side;

Once again, when discussing these colors, we use a tuple of three numbers, in the order of the abbreviation: hue, then saturation, then value.

One interesting side-effect of HSV is that there are an infinite number of black values, and an infinite number of white values. This is because rather than working on a group of integers, it works on a group of floating-point values. The hue may range from 0 to 360, but it is actually an angle along a circle, and as a result is infinite (though, in practice, this is typically limited to integer values). This means that HSV(0, 0, 0) is black, as is HSV(120, 0, 0), and HSV(240, 0, 0).

While we will not be using HSV in our examples, it is important to know that RGB is not the only color theme out there.

Alpha Component

One additional thing to discuss is the "alpha" component, which is effectively a "transparency" component, in RGB ranging from 0 to 255 typically. A value of 0 means that the color is completely transparent, and a value of 255 means the color is completely opaque.

Transparency just means "how much of what is behind this color is visible". We often use it to draw one image over another and show part of the image beneath.

Opening Images

Right, so let's get started. Before we can play with images, we have to open them. We have to read them into our software.

Opening and reading images in PHP is relatively straightforward. The GD API provides a bunch of imagecreatefrom<x> functions that work on specific file types. I've detailed a couple below:

  • imagecreatefrombmp: creates an image from a bitmap / .bmp file;
  • imagecreatefromgif: creates an image from a graphics interchange format / .gif file;
  • imagecreatefromjpeg: creates an image from a JPEG (joint photographic experts group) format / .jpeg, .jpg, .jpe, .jfi, .jfif, .jif file;
  • imagecreatefrompng: creates an image from a portable network format / .png file;

There are others, and if you have time I recommend exploring them. For the sake of our work, we'll only be dealing with those throughout the process.

Using all of these functions are basically the same, and relatively easy. Each one takes a single parameter which is the name of the file to open. If certain PHP extensions and features are enabled you can also provide a URL instead of a file name.

To make an image, we just pull it into a variable like normal:

$image = @imagecreatefromjpg($filename);

You may notice the at-sign (@) is new, we use @<function call> to suppress errors from the function. It's similar to a try / catch, but with an empty catch.

Error suppression

It's not often recommended, but we can use error-suppression on function calls to make life a bit easier. Instead of needing a bulky try / catch block, we simply use the @ to suppress the error.

The method behind the @ suppression operator is relatively tricky, but it basically sets the error-reporting level to 0 for that call. We've never discussed the error-reporting level before, but the basics are that PHP has several "levels" of errors. Some errors are considered 'fatal' and will stop script execution, some just print unpleasant output to our response.

Back to Opening Images

We use error suppression in our imagecreatefromjpg to ensure that if the image fails to load we can handle that specifically. Typically, we would do something like:

$image = @imagecreatefromjpg($filename);
if (!$image) { // test that the `$image` variable is set, in this case prove it's not set
    // the image failed to load, so we should display an error to the user
} else {
    // do something with the loaded image
}

Generally speaking, if the image fails to load we just assume that the file doesn't exist. There are other reasons it might fail to load, such as faulty permissions, but most of the time it's just not there.

Once we've opened the image, it will be a "resource" and we can start workign with it. But before we do that, we want to save it again to make sure that we can see our changes once we start making some.

Saving Images

To save images we use functions that, quite literally, do the opposite of opening them. Conveniently, they are named the following:

  • imagebmp: saves an image as a BMP;
  • imagegif: saves an image as a GIF;
  • imagejpeg: saves an image as a JPG/JPEG;
  • imagepng: saves an image as a PNG;

Now, these functions can do more than just save images to a file, these functions can also send images to a stream or output them to the browser.

To start with, we'll just save images back to a file. With each of these we just pass the image, then the filename to save to:

$result = @imagejpg($image, $filename);

All four functions have the same first two parameters, but imagejpg and imagepng take additional parameters that determine how the image should be compressed and other options. We won't worry about those for now, we're just going to pass the $image and a $filename.

Creating Blank Images

The last boring bit to talk about is creating images in PHP. Obviously it's not always the case that we have an image to start with, sometimes we want to "create" a new image from scratch.

Creating an image in PHP happens primarily with the use of two functions:

  • imagecreate: creates a "palette" based image;
  • imagecreatetruecolor: creates a true-color based image;

A "palette" based image has a fixed list of colors available, i.e. a "palette", and can only draw colors from that list. No other colors can be drawn to the screen. A true-color image can draw any color, whether it's in the palette or not.

There are also two functions for converting between the two:

  • imagepalettetotruecolor: converts a palette image to true-color;
  • imagetruecolortopalette: converts a true-color image to palette;

We're only going to look at true-color images, created via the imagecreatetruecolor function.

To create an image, we just supply imagecreatetruecolor with a $width and $height, which are integers that represent how wide and high the image is (the dimensions):

$image = imagecreatetruecolor(800, 600); // creates an 800x600 image, i.e. one of the older VGA resolutions

Much like before, we can manipulate and save this image.

Manipulating Images

Right, so on to the fun part. Let's start drawing on images!

Images are just a bunch of points, sometimes making shapes. PHP has a bunch of GD functions that make drawing shapes and text really easy. We're going to talk about them and see examples of how they work.

Fortunately, if we want to draw shapes, PHP has a bunch of GD functions that make doing so easy.

To begin, we have to create an image and create some colors for the image. I simply made red, green and blue as follows:

$image = imagecreatetruecolor(100, 100);
$color1 = imagecolorallocate($image, 255, 0, 0);
$color2 = imagecolorallocate($image, 0, 255, 0);
$color3 = imagecolorallocate($image, 0, 0, 255);

We're going to use this image for all of our tests, and we can imagepng it to a file to see how it looks.

Image Coordinates

The final thing to discuss briefly before we play with images is the coordinate system, or how points are represented.

In PHP GD, the top-left corner of an image is the coordinate (0, 0) ($x, $y). Going right, the $x value increases, going down the $y value increases. The bottom-right corner of our 100, 100 image will be (99, 99).

Coloring a single pixel

Before we draw shapes, let's learn how to draw a single pixel. (A pixel is just a point in the image.)

The PHP GD API has two functions we might care about here:

  • imagesetpixel: alters the color of a single pixel on the image;
  • imagecolorat: gets the color of a single pixel on the image;

If we wanted to set the middle-pixel in our 100, 100 image red, we would do something like the following:

imagesetpixel($image, 50, 50, $color1);

Drawing Shapes: Rectangles

The first, basic function to discuss is imagerectangle, which simply draws a rectangle shape on an image.

The function has 6 parameters: the first is the $image, the next two are the first corner of the rectangle ($x, and $y), the next two are the second corner of the rectangle ($x, $y), the last parameter is the color.

As an example, the following draws a red rectangle from the top-left corner of the image to the middle of the image:

imagerectangle($image, 0, 0, 50, 50, $color1);

In addition to imagerectangle, there is also an imagefilledrectangle which does the same, but fills the inside of the rectangle with the color as well. The previous function only draws the outline of a rectangle. If we wanted the same red rectangle, but wanted it filled internally, we would just use imagefilledrectangle instead:

imagefilledrectangle($image, 0, 0, 50, 50, $color1);

Drawing Shapes: Ellipses

Next, we can look at drawing ellipses / ovals. Like rectangle,s there are two functions. One draws a hollow ellipse, the other draws a solid ellipse.

To draw an ellipse, need to know three things: where is the center, how "wide" is it, how "tall" is it.

We then use the imageellipse function like the imagerectangle one:

imageellipse($image, 50, 50, 25, 25, $color1);

That draws a red ellipse at the center of our image spanning 25 pixels across and 25 pixels high, aka a circle with a radius of 12.5 pixels centered in our image.

To fill it, we use the imagefilledellipse function the same way:

imagefilledellipse($image, 50, 50, 25, 25, $color1);

Drawing Shapes: Polygons

Along with rectangles and ellipses, PHP GD also has the ability to draw polygons, or irregular / free-form shapes. As you may have guessed by now, we will start with the function to draw a hollow polygon:

imagepolygon($image, $points, size($points) / 2, $color1);

Unlikes the rectangle and ellipses functions, this one takes an array of points and an integer which is the number of points in the array. The points array is an array of each $x and $y coordinate of all the points.

The $points array is defined rather simply:

$points = [
    10, 10,
    10, 90,
    90, 90,
    90, 10
];

This defines a rectangle originating at (10, 10) in our image, moving to (10, 90), then (90, 90), then (90, 10). The shape will be closed by drawing a line from the last point to the first, which is convenient because that finishes our rectangle.

As expected, there is also a function to draw a filled polygon:

imagefilledpolygon($image, $points, size($points) / 2, $color1);

Drawing Shapes: Arcs

This next shape is a bit interesting: the arc.

An arc is just a curved line, but trying to draw one can be a bit complicated. In PHP, we solve this by having three main components of an arc:

  1. The center ($x, $y) of the arc;
  2. The size ($width, $height) of the arc;
  3. The sweep angle ($start, $end) of the arc;

The sweep angle of an arc is a value of 0 to 360, and represents the angle, in degress, of that position. The sweep angles need not be in order of low-to-high, as the arc drawing will start from the $start angle in the circle, and move clockwise to the $end angle of the circle.

Additionally, the 0 degree angle is the "three-o-clock" position, or due-right from the center, and as the angle increases it moves clockwise, to 360 being the same position again.

To imagine how an arc is drawn, imagine we begin to draw an ellipse with the center and size, then we only actually drew the angle from the start to end positions.

We draw an arc with the imagearc function, as follows:

imagearc($image, 50, 50, 50, 50, 0, 120, $color1);

This draws a red arc oriented around the center of our screen, with a radius of 25, beginning from angle 0 (the three-o-clock position) and ending at angle 120 (the seven-o-clock position).

As you might expect, there is also an imagefilledarc function, but it has an additional parameter after the $color that we must provide, which is the "style" of arc.

Because there are many ways we can close an arc, PHP defines several styles:

  1. IMG_ARC_PIE: this fills the arc by drawing lines to the center of the arc radius;
  2. IMG_ARC_CHORD: this closes and fills the arc by drawing lines to the center, and drawing a straight line between the arc ends, then discarding the curved portion of the arc;
  3. IMG_ARC_EDGED: this closes and fills the arc by drawing lines to the center of the arc radius;
  4. IMG_ARC_NOFILL: this only closes the arc when combined with any of the above, and does not fill the center of the shape;

It may seem unintuitive, but the IMG_ARC_NOFILL actually makes a lot of sense when you start drawing with IMG_ARC_CHORD or IMG_ARC_EDGED, as both of those "close" the polygon, whereas IMG_ARC_PIE only fills the inner area. I recommend experimenting with them, I've dropped a few different combinations below:

imagefilledarc($image, 25, 25, 50, 50, 0, 120, $color1, IMG_ARC_PIE);
imagefilledarc($image, 25, 25, 50, 50, 120, 240, $color2, IMG_ARC_CHORD);
imagefilledarc($image, 25, 25, 50, 50, 240, 360, $color3, IMG_ARC_EDGED);
imagefilledarc($image, 25, 75, 50, 50, 0, 120, $color1, IMG_ARC_PIE | IMG_ARC_NOFILL);
imagefilledarc($image, 25, 75, 50, 50, 120, 240, $color2, IMG_ARC_CHORD | IMG_ARC_NOFILL);
imagefilledarc($image, 25, 75, 50, 50, 240, 360, $color3, IMG_ARC_EDGED | IMG_ARC_NOFILL);
imagefilledarc($image, 75, 75, 50, 50, 0, 120, $color1, IMG_ARC_PIE | IMG_ARC_EDGED | IMG_ARC_NOFILL);
imagefilledarc($image, 75, 75, 50, 50, 120, 240, $color2, IMG_ARC_CHORD | IMG_ARC_EDGED | IMG_ARC_NOFILL);

These combinations can be drawn on our (100, 100) image, and show how the different arc filling techniques can have interesting effects.

Filling Regions

The next thing to talk about is 'filling' an image.

PHP uses a technique called "flood fill" to fill regions with certain colors. This allows you to specify a starting point, then PHP will start replacing the color it finds at that point in the image with the provided color, working adjacent pixels until the entire contiguous region is filled.

For example, the following will draw a red rectangle, then fill the inside of the rectangle with green:

imagerectangle($image, 25, 25, 75, 75, $color1);
imagefill($image, 50, 50, $color2);

There is also imagefilltoborder, which takes a border color as the first color argument, and will fill any color until it reaches the border color.

As an example, the following draws our red/green square, then fills the entire square with blue, as we set our $border_color to the RGB(0, 0, 0) background color:

$background = imagecolorallocate($image, 0, 0, 0);
imagerectangle($image, 25, 25, 75, 75, $color1);
imagefill($image, 50, 50, $color2);
imagefilltoborder($image, 50, 50, $background, $color3);

In Conclusion

We're going to stop here, because we've reached my target character limit. We still have a couple more GD items to cover, as GD is a huge API surface. We'll be wrapping the main points of GD up in the next article, and then doing some more object-oriented programming, including defining classes that handle a lot of our GD work.

I know I ended this post rather abruptly, and it didn't flow quite as well as my normal writing does...there are a bunch of reasons for that, but the root is that I'm in a mental rut right now. Our society (at least, here in the U.S.) has always been a bit of a travesty, but lately we're seeing that our society truly is as deplorable, racist, sexist, homophobic, xenophobic and downright disgusting as it's been made out to be. For a while I lived my life thinking we were "the greatest country", but lately I've realized we're not even close.

I'm saying all this because it's important that we acknowledge that, as a society, we have a lot of work to do. I truly hope more people can open their eyes and see the mistakes we've made, and work to correct them. We truly can change things if we work for it, but we all have to work for it. We can't give up...

With that said, please keep safe, and help us beat COVID-19 and the systemic injustice and failings of our "Justice" system. #BlackLivesMatter

Getting libvirt networking in place without too much pain

Automating libvirt networking with minimal pain

A few weeks ago I started a project to experiment with kvm / libvirt / qemu virtualization on CentOS 8.2. Getting qemu and libvirt all setup was relatively easy, and went pretty painlessly. After that, setting up network bridges went relatively well also.

However, when it got to networking, things got a little less pleasant.

Non-bridged Networking

First and foremost, the project I'm working on treats the hypervisor as the firewall. It does minimal routing to ensure that the VM's are adequately separated from the main network. Documentation on this was limited, to say the least. I won't go into the setup details, but you basically create virtual interface bridges via the virsh net-define from an XML file.

Next, the hard part: creating firewall rules to allow certain services on VM's to be accessed.

This proved a much more challenging task than I thought. All the information out there made this extremely daunting and non-trivial.

As it turns out, the recommended solutions are comparatively over-engineered to what I'm going to propose.

Setting up the qemu hook

First and foremost: when libvirt starts, stops, and migrates a VM it calls the qemu hook which is, by default, in /etc/libvirt/hooks. There are two parameters for this hook we're going to care about: ${1} which is the "domain" or "virtual machine", and ${2} which is the "action" being performed.

For our situation, we care about three actions: start, stopped, and reconnect. For reconnect, we're simply going to stop then start.

VM / Networking Structure

My setup puts all my VM info in /home/kvm, there are three folders there:

  • /home/kvm/images: all my ISO's;
  • /home/kvm/disks: all of the disk images for my VM's;
  • /home/kvm/networks: all of the scripts for the VM's of which we should do network operations;

I chose this structure because it's easy to remember.

Now the biggest problem with libvirt networking is that it resets firewall rules in iptables regularly. Any time the daemon restarts, if I recall correctly. As a result, if we create iptables rules there is no guarantee they will remain after any sort of state-change.

The libvirt hook

So, my solution was to put a network script for each VM in /home/kvm/networks, named after the VM, that allows me to add/remove the appropriate iptables rules when the VM starts and stops.

To do so, I created a very simple qemu hook:

#!/bin/bash

DOMAIN=${1}
ACTION=${2}
BASE_PATH="/home/kvm/networks/$DOMAIN"

if test -f $BASE_PATH; then
    if [ $ACTION = "stopped" ] || [ $ACTION = "reconnect" ]; then
        $BASE_PATH stopped
    fi
    if [ $ACTION = "start" ] || [ $ACTION = "reconnect" ]; then
        $BASE_PATH start
    fi
fi

This hook simply calls /home/kvm/networks/VM_NAME with either start or stopped, depending on what is happening.

Next, we'll look at a sample VM I have a script for.

Important: after creating this hook, restart libvirtd via service libvirtd restart. Otherwise, libvirt will not pick up on your new script.

The VM Script

The script for each VM is extremely simple, I define the public / external IP, the private / internal IP, and the ports I want to forward, in the format protocol:port, with commas between each combination:

#!/bin/bash
PUBLIC_IP=192.168.0.2
PRIVATE_IP=10.1.1.2
PORTS=tcp:80,tcp:443
/home/kvm/networks/modify_network $PUBLIC_IP $PRIVATE_IP $PORTS ${1}

The PUBLIC_IP is an external IP, this IP should be assigned to a physical interface on the server.

We then call another script, /home/kvm/networks/modify_network, which has the root of the iptables rules. We also pass ${1} which is either start or stopped (because that's how qemu calls it).

The hard part: the iptables rules

Ok, so maybe this isn't that hard. Again, we have a relatively simple script:

#!/bin/bash
PUBLIC_IP=${1}
PRIVATE_IP=${2}
readarray -d , -t PORTS <<< "${3}"

if [ ${4} = "start" ]; then
    /sbin/iptables -I FORWARD -m state -d $PRIVATE_IP/32 --state NEW,RELATED,ESTABLISHED -j ACCEPT
    for (( n=0; n < ${#PORTS[*]}; n++ )); do
        readarray -d : -t PORT_PARTS <<< "${PORTS[n]//[$'\t\r\n']}"
        /sbin/iptables -t nat -I PREROUTING -p ${PORT_PARTS[0]} --dport ${PORT_PARTS[1]} -d $PUBLIC_IP -j DNAT --to $PRIVATE_IP
    done
fi

if [ ${4} = "stopped" ]; then
    /sbin/iptables -D FORWARD -m state -d $PRIVATE_IP/32 --state NEW,RELATED,ESTABLISHED -j ACCEPT
    for (( n=0; n < ${#PORTS[*]}; n++ )); do
        readarray -d : -t PORT_PARTS <<< "${PORTS[n]//[$'\t\r\n']}"
        /sbin/iptables -t nat -D PREROUTING -p ${PORT_PARTS[0]} --dport ${PORT_PARTS[1]} -d $PUBLIC_IP -j DNAT --to $PRIVATE_IP
    done
fi

This one is, perhaps, the most complex so far, but only because I wanted comma-specified ports.

The readarray -d , -t PORTS <<< "${3}" section reads the third parameter, then splits it on commas into an array called PORTS.

Then, if the fourth parameter is start, we do an /sbin/iptables -I FORWARD insert with the private IP. This is actually extremely important, as libvirt by default only allows for RELATED,ESTABLISHED connections by default, and we need to allow NEW connections as well. I spent probably three or four days fighting this rule specifically.

Next, we use the for to loop through all the items in the PORTS array. (The ${#PORTS[*]} counts the number of elements in the array.) The readarray -d : -t PORT_PARTS <<< "${PORTS[n]//[$'\t\r\n']}" line does two things: the part of the line after the carets (<<<) removes whitespace, then the readarray section again splits the string, this time on colons.

We then do an /sbin/iptables -t nat -I that opens the protocol and port to the public and private IP combination we had.

Lastly, our stopped block does the opposite: it removes each rule we previously added to ensure that no errant ports are left open if the VM is killed.

Verify with iptables

Lastly, it's helpful to verify the iptables -L result:

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             10.1.1.2             state NEW,RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             10.1.1.0/24          ctstate RELATED,ESTABLISHED
ACCEPT     all  --  10.1.1.0/24          anywhere
ACCEPT     all  --  anywhere             anywhere
REJECT     all  --  anywhere             anywhere             reject-with icmp-port-unreachable
REJECT     all  --  anywhere             anywhere             reject-with icmp-port-unreachable

I left the default rules in place, but you'll notice that the second rule, which allows connections from anywhere to 10.1.1.0/24, only allows RELATED,ESTABLISHED, which means that if you are opening an internal service users won't be able to connect unless we add our NEW,RELATED,ESTABLISHED rule, as there's no previous connection to associate yet.

We'll also verify the iptables -t nat -L result:

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       tcp  --  anywhere             192.168.0.2          tcp dpt:https to:10.1.1.2
DNAT       tcp  --  anywhere             192.168.0.2          tcp dpt:http to:10.1.1.2

These both show that our rules were created.

The End

This all is basically the result of my two+ weeks of researching this issue. During my research I came across dozens of articles that had much more complex solutions, but in the end my requirements (NAT'ing specific ports to specific VM's) actually simplified my solution.

Getting Started with PHP: Part 4

Getting started with PHP: Part 4

Last time we went over more of the PHP function details. Particularly, we covered the following:

  • Default values in functions;
  • Functions can be late-defined;
  • Functions can have variable-length arguments;
  • Accessing global variables in functions / scoping;
  • Standard lib: array <-> string;
  • Standard lib: trimming strings;
  • Standard lib: searching strings;
  • Standard lib: manipulating strings;
  • Standard lib: hashing strings;

This set us up to know more about how PHP functions work, what kinds of functions we can expect, and advanced features of function definitions. This time, we're going to go into something called "object-oriented programming", and talk about a PHP feature known as "classes".

What are classes

A "class" in PHP is a collection of values and functions that typically perform an individual behaviour. We mentioned them a while back in lesson 2 when I talked about HTML form controls, and that I have a PHP library of my own which defines a plethora of these functions.

Obviously one of the issues with putting everything in the PHP global scope is that you run into trouble when you have two functions but they would have a similar name. In these cases, you end up with things like:

function user_is_valid(int $user_id): bool
function post_is_valid(int $post_id): bool
function comment_is_valid(int $comment_id): bool
function email_is_valid(string $email): bool

Classes allow us to put some logical barriers between them. They allow us to name all these functions is_valid, but put them in different locations to ensure the names don't conflict with each other.

Very often, we'll use classes to encapsulate groupings of functions and pieces of data. We might make a class for a User component, a class for a Database component, a class for Html helpers, for Parsing data, a class of String functions, etc.

The first component of classes we'll talk about are "instance" functions. These functions require you to have created a "version" or "copy" or "instance" of the class.

PHP: Classes

Before we can talk about an "instance function" in a class, we actually have to talk about how to define a class. In PHP, classes are defined rather simply: the class keyword, followed by a name, then curly-braces ({}) to define what exists within the class:

class HtmlControls {
}

At this point, if we use a sufficiently-advanced development tool, when we type Html it will give us HtmlControls as an option, though we currently have no logic in it.

There are some additional things you might see after the name and the opening brace ({): extends $name, where $name is the name of another class, and implements $name, where $name is the name of an interface. Ignore these for now, we'll talk about them later.

In PHP, we try to ensure that a class has a "single" responsibility and name it appropriately. A "single" responsibility just means that the class has one "job" or "goal". The idea is that the class is supposed to do "a" thing, is all.

PHP: Classes - Instance Functions

So now that we know what a class is there are two ways we define things in the class: either as a "static" or "always accessible" member, or an "instance" member. We'll start with instance members, because they allow us to dive into classes at a more reasonable pace.

We define members in a class much in the same way we would in the global scope. To start with, we define functions with the function keyword, then the exact same semantics as global functions. Thus, to put our checkbox function in our class, we just define it like normal:

class HtmlControls 
{
    function checkbox(string $name): ?string {
        if (isset($_POST[$name]) && $_POST[$name] !== "off") {
            return $_POST[$name];
        }
        return NULL;
    }
}

We defined this function as a standard "instance" function, and by default PHP treats these as "public" functions, but we'll talk about that in a little bit.

To call this function, we have to create an instance of the class and then call the function. To create an instance, we define a variable to hold the instance, then use new $class_name to create it:

$controls = new HtmlControls();

At this point, we can call any instance functions with $controls->$function_name. So, to call our checkbox function, we simply do $controls->checkbox($checkbox_name).

PHP: Classes - Static Functions

We typically use instance functions to define behavior that should be tied to data within the class. However, in our case, our behavior isn't tied to other data within the class, so an instance function doesn't necessarily make sense. In this case, we can use a static function.

To define a static function we just put the word static before the word function in our definition:

class HtmlControls
{
    static function checkbox(string $name): ?string {
        if (isset($_POST[$name]) && $_POST[$name] !== "off") {
            return $_POST[$name];
        }
        return NULL;
    }
}

There is a difference in how we call these functions. While we have to new HtmlControls and then ->checkbox($checkbox_name) for an instance function, static functions are a bit easier to call. For a static function, we simply $class_name::$function_name for static functions. In our example, that would be HtmlControls::checkbox($checkbox_name).

In our case, a static function makes more sense because the behaviour we are defining doesn't depend on any other items within our class.

The next thing to discuss is class data. We discussed the is_user_valid function before, when we talked about global scope, but now we'll put it into the context of a class.

PHP: Classes - Values / Variables

In order to have functions work with class data, we have to talk about class data. To define variables in a class, we define them with a visibility modifier (private, protected, public) then a variable name.

class User
{
    public int $id;
    public string $name;
}

But, I don't want to just throw those names at you, so we're going to talk about visibility modifiers really quick.

PHP: Visibility Modifiers

PHP has three visibility modifiers:

  • private: the item is only usable by any other items within the same class;
  • protected: the item is only usable by any other items within the same class or any derivative class;
  • public: the item is usable by items within and outside the class;

Many times, to properly encapsulate object-oriented data, we will actually define class variables as private, and then define public functions to get and set the data. The reason for this is that if we have to validate the data when it gets set, we don't want to change the behavior of all the callers unless we need to.

PHP: Back to Classes - Values / Variables

With visibility modifiers out of the way, we can talk about how we might access them outside the class. Much like accessing functions, we use $variable->$name:

$user = new User();
$user->name = "Elliott";
echo $user->name;

Just like any variable, using $this->$name = $value will set it, and omitting the = $value will "get" it.

As mentioned, we want to encapsulate these variables in a different manner, specifically to prevent the need to change behavior everywhere we use them if we ever add validation logic. To do that, we'll discuss accessing instance data from another instance member.

PHP: Classes - Accessing Instance Data

We know how to define instance variables, we need to discuss how we access them. Unlike standard variables, they aren't accessed by using $name. We actually have to access them with a special "instance" object, called $this.

This means we can change our $id and $name variables to private, and define a getId and getName function that will return these values:

class User
{
    private int $id;
    private string $name;

    public function getId(): int {
        return $this->id;
    }
    public function getName(): string {
        return $this->name;
    }
}

So now, we can get these values, but we also need to be able to set them:

public function setId(int $id) {
    $this->id = $id;
}
public function setName(string $name) {
    $this->name = $name;
}

Once that's done, we would set and get our name with a minor change to our code:

$user = new User();
$user->setName("Elliott");
echo $user->getName();

All that out of the way, we can put our isValid function back in:

public function isValid(): bool {
    return $this->id !== 0 && $this->name !== "";
}

But, there's one more major component of classes I want to talk about: constructors.

PHP: Classes - Constructors

The next point about classes to discuss here is constructors. Constructors allow us to specify that the new $class_name() call requires parameters, and means we can remove our need to $user->setName() by including the $name in the constructor.

In PHP we accomplish constructors via a magic function called __construct.

We define this function in our class like any regular function, but the special name __construct causes it to be seen by any new $class_name() calls. This means we can force callers to provide certain things when they create an instance of the class, ensuring that certain behaviour is guaranteed from the beginning.

For our User class, it might look something like this:

public function __construct(int $id, string $name) {
    $this->id = $id;
    $this->name = $name;
}

Now, when we try to instance a new User, we will need to use that constructor. It forces us to provide the $id and $name on creation of a User.

$user = new User(1, "Elliott");

We can still use the default values on parameters if we like, which means we can create a constructor that requires some, or even none of the values to be provided. This is because __construct is a regular function, just with a special name.

PHP: Classes - Constants

Lastly, I want to cover constants in classes. Constants can be used to provide data that doesn't change to callers. They're defined with the const keyword as well as whichever access modifier is appropriate. Additionally, the dollar-sign is not used for constant definitions.

In PHP, I typically like to write my constant names as UPPER_SNAKE_CASE. As an example, we might keep a constant for the table name that we store user data in, that way anywhere we use it we make sure it's always the same. This means if we ever change the user table name, we don't need to change it everywhere we use it, we only change it in the one location.

To define our constant, we just put it in our class:

private const USER_TABLE = "Users";

The purpose of constants is to create a single source of truth so that you cannot use the wrong value, or forget the value you were using.

PHP: Including Other Code

At this point, it's time to discuss how we include other code within our PHP application. Putting all of our code in one big file is not always the best way to build our application. A very common and good practice in software engineering is to separate individual components of an application into reasonable chunks. Yes, we could put all of our code in a single, enormous file. But that makes it more difficult to troubleshoot and modify in the future.

So, with PHP this is actually a well-thought-out process. There is a group of 4 functions that each play a role.

With PHP, we have the ability to write something in one PHP file, and then include that something in another PHP file. The method to do so depends on what exactly is being accomplished, but there are two pairs of functions we use: include, include_once, and require, require_once.

The include and include_once functions will pull another PHP file into the calling file. The include_once function will guarantee that the file does not appear more than once. This can be important if you include a file in multiple places throughout your application. In some cases, you may wish to ensure that it is only included a single time, because it might have class definitions or function definitions that cannot appear more than once.

The require and require_once functions will do the same thing, with the exception that they will throw a fatal error if the file cannot be included for some reason. If your application depends on something to the point where it cannot work without it, you likely want require and require_once. I find that, most commonly, I use require_once, and if something can appear more than once I use require.

The reason that the _once version exist is because you can't redefine things, as we've mentioned briefly before. As an example, imagine you have the following:

class MyClass
{
    function myFunction(): string {
        return "Hello World";
    }
}
class MyClass
{
    function myFunction(): string {
        return "Hello World";
    }
}

PHP will error on the second class MyClass line, because you redefined this. With require and include that stands true as well. This makes it critical to understand how to protect your PHP code from this issue.

As an example, imagine we have the following structure:

Database.php

<?php
require_once("User.php");

class Database
{
    function getUser(int $id): User {
        return new User($id);
    }
}

User.php

<?php
class User
{
    private int $id;

    public function __construct(int $id) {
        $this->id = $id; 
    }

    public function getId() {
        return $this->id;
    }
}

ViewUser.php

<?php
require_once("User.php");
require_once("Database.php");

if (isset($_GET["id"])) {
    $database = new Database();
    echo $database->getUser($_GET["id"])->getId();
}

With this example, we can build the following tree of includes:

ViewUser.php
|-- User.php
|-- Database.php
    |-- User.php

Because we used require_once instead of require, the User.php file will not actually be included by the Database.php file, but instead by the ViewUser.php file. Additionally, we won't get any errors about duplicate definitions.

However, if we changed that to require, the application would fail on require_once("Database.php") because it would attempt to include the User.php file again, and would duplicate the class User definition.

PHP: Interfaces

The next major object-oriented discussion point is interfaces.

Interfaces are a way of providing a common specification for implementations to fulfill. The idea is that you can design a specification that you need, and then allow implementors to switch out how they fulfill it.

As an example, we may have a situation where we have a "data provider". The "data provider" may provide functions to read and write various objects, but it might provide them for different storage platforms. I.e. we may have a MySqlDataProvider, a PostgreSqlDataProvider, an XmlFileDataProvider, and so on.

To do this, we might have an interface that defines what our data provider must do.

Much like a class, an interface is defined using the interface keyword, then the name of the interface. I like to prefix my interface names with an I, it comes from a naming convention for a different language, but it also allows you to look at the name and understand that you're not actually dealing with a hard-class.

However, unlike a class, you cannot create an instance of an interface. You cannot new $interface_name(), because an interface does not have any functionality. It is strictly a list of "requirements". In some languages it's also called a "protocol".

Once you've defined the interface via name, you identify the functions and variables that are part of the interface requirements. This is done by defining the function like normal, but not providing an implementation via curly-braces ({}) but simply ending with a semi-colon (;).

For a data provider, we might define the following interface:

interface IDataProvider
{
    function loadUser(int $id): User;
    function saveUser(User $user);
}

At this point, we have created the protocol or specification, and we can now implement it. To do so, we'll define a class that implements the interface via the implements keyword. This is added after the class name and before the opening curly-brace ({), and afterwards we include the names of the interfaces we want to implement.

class XmlDataProvider implements IDataProvider
{
    function loadUser(int $id): User {
        return new User($id, "Elliott");
    }
    function saveUser(User $user) {

    }
}

Obviously we didn't load or save the User, but we do have the structure for doing so. Now we can instance the XmlDataProvider:

$data_provider = new XmlDataProvider();

Now, at this point, it might not be clear why we used the interface at all: we only have one data-provider, and we create it directly, the only place we see IDataProvider is the implements keyword.

The purpose of the interface is to allow us to create functions and other classes that use the interface definition, so that we can provide any class that implements the interface.

function saveUserFromForm(IDataProvider $data_provider) {
    $id = $_POST["id"];
    $name = $_POST["name"];
    $user = new User($id, $name);
    $data_provider->saveUser($user);
}

Now, we can give the function any IDataProvider, such as our XmlDataProvider:

saveUserFromForm($data_provider);

This means that if we later built a MySqlDataProvider, we can literally swap what gets passed to the saveUserFromForm function, and we would automatically be using our new provider.

The bigger concept here is "abstraction": that is we move common or higher-level functionality into another layer of our application. Something like the data provider details don't matter to the users of those components. The users only care that the component works and that they can use it.


As It Stands

At this point we actually have enough information to start building real things. We'll go over new topics as we encounter them, and as we build stuff we'll learn a lot. We have a lot more information to cover: from more standard-library functions and classes to more syntax bits we have to learn yet. That said, we're going to start building real-world stuff next, and hopefully that demonstrates how powerful PHP is.

Getting started with PHP: Part 3

Getting started with PHP: Part 3

Previously, we discussed some more of the fundamental components of PHP:

  • Strings have 4 ways to be built;
  • Magic superglobals: $_POST and $_GET;
  • Basic arrays;
  • Creating and calling functions;
  • Logical combination operators (&&, ||, !, xor);

We also discussed some bits of HTML:

  • Forms;
  • Input types: text, textarea, checkbox, radio, select;

More Function Bits

We touched on functions last time, but we have so much more to cover in that area alone. Functions in PHP are extremely powerful, and there are a lot of features I want to cover so that you are familiar with them.

PHP: Default Values for Function Parameters

One of the features of PHP functions is the ability to specify default values for parameters. This means we can build functions that have values we may or may not need to provide.

PHP accomplishes this in a rather simple, and self-explanatory manner: simply add an equal sign (=) and then the default value after the parameter name:

function greet(string $name, bool $all_caps = FALSE): string {
    if ($all_caps) {
        $name = strtoupper($name);
    }
    return "Hello $name";
}

We can call our function with or without the optional values:

$name = "Elliott";
echo greet($name) . "<br />"; // "Hello Elliott"
echo greet($name, FALSE) . "<br />"; // "Hello Elliott"
echo greet($name, TRUE) . "<br />"; // "Hello ELLIOTT"

As we see, just providing $name is equivalent to providing FALSE for the second value because of our default specification.

Normally, I would advise against default values, but with PHP sometimes it becomes necessary. Many languages have something called "overloading", where you can create more than one function with the same name but different parameter, but PHP does not have that feature, so we use optional parameters with default values instead.

PHP: Late-defined Functions

PHP supports the idea of defining functions after another function is called. This is either nesting functions, or late-defined functions. The functions don't exist initially, but can be created after another function is called.

Doing this is rather straightforward, simply define the function you want to be created within another function.

function f1() {
    function f2() {
        echo "Test";
    }
}

In this example, function f2 does not exist until f1 is called.

I've never seen much usage of this feature in the past, but I think it's important to talk about because if you do see this I want it to look familiar.

PHP: Variable-length Arguments

Another feature of PHP is variable-length arguments, or, indicating that the function can take any number of parameters.

The method for doing this is to specify three dots before the final parameter: ...$values.

function sum(int ...$values): int {
    $result = 0;
    foreach ($values as $value) {
        $result += $value;
    }
    return $result;
}

The variable will be treated in the function as an array, but the type should be specified as the plain type of the data. Here, we specified we expect an int, then that any number of integers can be provided.

This can be called in two ways: separate each value with a comma, provide an array and use the same triple-dot to pass it:

echo sum(1, 2, 3, 4) . "<br />";
echo sum(...[1, 2, 3, 4]) . "<br />";

PHP: Accessing global variables in functions

Once you start playing with functions, you might find that there's a certain task that becomes problematic: accessing global (not superglobal) variables within a function.

As an example, imagine we have a function that works on a user:

$user_id = 1;
$user_name = "Elliott";
function is_user_valid() {
    return $user_id !== 0 && $user_name !== "";
}
echo is_user_valid() . "<br />";

If you run this, you will get two errors:

Notice: Undefined variable: user_id in $file on line $line

Notice: Undefined variable: user_name in $file on line $line

This is telling us that, while our code looks reasonable, PHP can't access $user_id or $user_name in the function.

This is because those variables are globals, and we have to tell our function how to access them.

PHP: Scoping

PHP has a concept of "scoping", or: data is only available to the scope it is in, or a scope within that scope. We can see this with the function example above: the curly-braces ({}) create a new scope, and we lose access to certain things.

The "global" scope is a special scope in PHP, and anything that is not in a class (we'll talk about those later) or function is part of this "global" scope. This means our $user_id and $user_name variables are in the "global" scope.

Beyond that, PHP scopes at the namespace, class and function levels. Classes can only access other classes in the same namespace or a parent by default, functions can only access data in that function or class by default. This means that to share data between functions or classes you have to pass that data from one to the other.

Back to Accessing the Globals

So, with that said, we need to find a way to access those two variables within our function. One method would be to pass them in, and 99% of the time that is the method you should choose, but we're going to see how we can avoid that.

The first way to pull global values into our local function is to use the global keyword and then name the variables that are defined in the global-scope. In our example, we would do the following:

function is_user_valid() {
    global $user_id, $user_name;
    return $user_id !== 0 && $user_name !== "";
}

Here, global $user_id, $user_name; says that those two variables are pulled from the global scope. I prefer this method if I must access global values, as it feels the cleanest, and with the IDE I use (PhpStorm, if anyone is curious) I get auto-completion for them.

The next method is to use another special superglobal called $GLOBALS (yes, quite "on the nose"). This superglobal takes a string index like an array, which is the variable name (without the dollar-sign in front of it): $GLOBALS["user_id"] and $GLOBALS["user_name"].

This would leave us with the following function:

function is_user_valid() {
    return $GLOBALS["user_id"] !== 0 && $GLOBALS["user_name"] !== "";
}

I'm not a huge fan of this method, I prefer the global <names> method if I must use globals in a function, but once again I want you to be aware of all the options out there.

PHP: Let's talk about some built-in functions

So, we've talked about how we can write our own functions, I think it's time we talk about some built-in PHP functions.

One thing I want to mention: PHP didn't always have a naming-convention, so some PHP functions are what we call "snake case": snake_case, some are "all lower letters" alllowerletters. This can be confusing at times, but it's mostly because PHP grew organically.

What do I mean by "organically"? In the early days, PHP only added things when they were needed. Instead of trying to over-engineer the language, functions were added by the people who needed them when they were needed, and because there was no naming convention they were named by the people who needed them.

As a result of this, you might find that function names don't always seem consistent. This is a common gripe of various communities, both PHP-oriented and not. Some folks like to use it as an excuse to talk trash about PHP, but it's just a by-product of PHP being one of the early languages to allow the users of the language to commit back to it.

PHP is a very string-oriented language, which makes sense because it's primarily a web language, and strings are the language of the web. So, as a result, we're going to talk about a lot of string-related functions.

PHP: Functions for Arrays and Strings

There are three major string/array functions in PHP I want to talk aboue:

  1. explode - split a string on characters;
  2. implode - combine an array of strings with a delimiter;
  3. str_split - split a string into groups of characters;

So explode and implode are basically opposites of each other: one turns a string into an array, the other turns an array into a string. Both use some sort of "delimiter" to split or combine on.

The syntax for each is rather straightforward: explode(string $delimiter, string $string): array, implode(string $separator, array $array): string.

Additionally, implode does not require the $separator to be provided, instead it can be omitted and the string will be concatenated with no separators.

We'll go through a quick example of explode and implode to see how they work:

$my_name = "Elliott";
$some_str = "Hello $my_name, how are you doing?";
echo $some_str . "<br />";
// Hello Elliott, how are you doing?

$parts = explode(" ", $some_str);
print_r($parts);
echo "<br />";
//Array (
//    [0] => Hello
//    [1] => Elliott,
//    [2] => how
//    [3] => are 
//    [4] => you
//    [5] => doing?
//)

$some_str = implode(" ", $parts);
echo $some_str . "<br />";
// Hello Elliott, how are you doing?

Right, so next we'll discuss str_split really quick. I'll admit, the name on this one isn't exactly self-explanatory, but what it does is split a string into groups of characters with a specified length.

$str_groups = str_split($some_str, 3);
print_r($str_groups);
echo "<br />";
//Array
//(
//    [0] => Hel
//    [1] => lo
//    [2] => Ell
//    [3] => iot
//    [4] => t, 
//    [5] => how
//    [6] =>  ar
//    [7] => e y
//    [8] => ou
//    [9] => doi
//    [10] => ng?
//)

As we see, the string split into groups of 3 characters. This function is handy if you have block-data that you need to group up, such as hexadecimal values. Such as: str_split("0x1234", 2) will split into ["0x", "12", "34"].

There is another method for splitting strings in PHP, but we're not going to discuss it yet because it involves regular expressions, and I want to cover those at a later date.

PHP: Trimming Strings

The first three functions I want to talk about are ltrim, rtrim and trim. All three of these functions have a similar goal: they remove whitespace characters (or, if you choose, a group of characters you specify) from the beginning, end, or both of a string.

The ltrim function removes characters from the beginning, the rtrim function from the end, and trim is effectively ltrim(rtrim($value)).

$str = "  Test String with Whitespace  ";
$str_l = ltrim($str); // "Test String with Whitespace  "
$str_r = rtrim($str); // "  Test String with Whitespace"
$str_t = trim($str); // "Test String with Whitespace"

Additionally, all three take a second "mask" parameter:

$str = "123Test String with Whitespace321";
$mask = "123";
$str_l = ltrim($str, $mask); // "Test String with Whitespace321"
$str_r = rtrim($str, $mask); // "123Test String with Whitespace"
$str_t = trim($str, $mask); // "Test String with Whitespace"

The "mask" is just a string with every character you want to trim from the beginning / end. You'll also notice that the mask doesn't need to be in any particular order: ltrim / rtrim / trim will use it to build a list of characters to remove.

PHP: Searching Strings

PHP has quite a few functions for searching strings. The main ones we'll discuss are: strstr, stristr, strpos, stripos, strrpos, strripos, substr, substr_count. Note: I left the regular-expression based functions out again, as these are going to be covered later.

The first two functions we'll discuss are strstr and stristr. Both of these find a string within another string (thus, searching for a str in a str, hence the name strstr). The only difference is that stristr is case-insensitive. Both of these return either the string + everything after it, or if you specify the value TRUE for the optional parameter $before_needle it will return the part before the located string:

$my_name = "Elliott";
$some_str = "Hello $my_name, how are you doing?";
$result = strstr($some_str, $my_name); // "Elliott, how are you doing?"
$result = strstr($some_str, $my_name, TRUE); // "Hello "

One last note: both of these will find the first occurrence of the search.

The next functions I want to discuss are strpos, stripos, strrpos, strripos. The strpos function is the same as strstr, with the exception that it returns the position of the string it located, rather than returning the string. The strrpos function does the same, except it searches the string backwards.

All of these also take an optional integer parameter that indicates where to start searching the string. If the integer is greater than 0, it will start searching at that many characters from the beginning of the string. If the number is negative, it will start searching from that many characters from the end of the string.

$result = strpos($some_str, $my_name); // 6
$result = strrpos($some_str, $my_name); // 6

The stripos and strripos are the case-insensitive versions.

Next, we have substr, which will return a part of a string from a position for a given length. If the $length parameter is not provided then the entire string from the start position to the end will be returned.

$result = substr($some_str, 6); // "Elliott, how are you doing?"
$result = substr($some_str, 0, 5); // "Hello"

Lastly, substr_count, which will search a string for another and return how many times it appeared.

$result = substr_count($some_str, $my_name); // 1

PHP: Basic string manipulation

There are some basic manipulation functions I want to talk about, things that are relatively simple. I want to go over strrev, str_rot13, wordwrap, lcfirst, ucfirst, ucwords, strtolower, strtoupper.

The first function on the list, strrev, is a function that reverses a given string. The str_rot13 function rotates the letters of the string by the ceaser cipher, with a => n, m => z.

$result = strrev($some_str); // "?gniod uoy era woh ,ttoillE olleH"
$result = str_rot13($some_str); // "Uryyb Ryyvbgg, ubj ner lbh qbvat?"

The wordwrap function will wrap the string on words to be no longer than the given width. There are three optional parameters: the first is the maximum width of a line in characters and defaults to 75, the second parameter is a string separator to use and defaults to "\n", the third and final parameter is whether or not to cut up long words and defaults to FALSE.

$result = wordwrap($some_str, 10); // "Hello\nElliott,\nhow are\nyou doing?"

Next, lcfirst and ucfirst both change the case of the first character in the string. The lcfirst function will make it a lower-case character, and the ucfirst function will make it an upper-case character.

$result = lcfirst($some_str); // "hello Elliott, how are you doing?"
$result = ucfirst($some_str); // "Hello Elliott, how are you doing?"

The ucwords function changes the case of the first letter of each word to upper-case.

$result = ucwords($some_str); // "Hello Elliott, How Are You Doing?"

Lastly, in this group, we have strtolower and strtoupper, which convert the entire string to all lower-case or all upper-case respectively.

$result = strtolower($some_str); // "hello elliott, how are you doing?"
$result = strtoupper($some_str); // "HELLO ELLIOTT, HOW ARE YOU DOING?"

PHP: String Hashing

PHP includes a pretty large quantity of functions built to hash strings. Note: none of the ones we are discussing in this section are suitable for passwords, we will discuss password-hashing on its own at a later date.

We're going to discuss all these as a group, as the only major differences in them are the particular algorithm.

While the string hashing functions we're talking about are not suitable for hashing passwords, they are suitable for data validation and message digest validation.

The hash algorithms supported by PHP can be determined by using the hash_algos() function. You can print_r this function if you need a quick list for reference. The list my computer generated is as follows:

//Array
//(
//    [0] => md2
//    [1] => md4
//    [2] => md5
//    [3] => sha1
//    [4] => sha224
//    [5] => sha256
//    [6] => sha384
//    [7] => sha512/224
//    [8] => sha512/256
//    [9] => sha512
//    [10] => sha3-224
//    [11] => sha3-256
//    [12] => sha3-384
//    [13] => sha3-512
//    [14] => ripemd128
//    [15] => ripemd160
//    [16] => ripemd256
//    [17] => ripemd320
//    [18] => whirlpool
//    [19] => tiger128,3
//    [20] => tiger160,3
//    [21] => tiger192,3
//    [22] => tiger128,4
//    [23] => tiger160,4
//    [24] => tiger192,4
//    [25] => snefru
//    [26] => snefru256
//    [27] => gost
//    [28] => gost-crypto
//    [29] => adler32
//    [30] => crc32
//    [31] => crc32b
//    [32] => crc32c
//    [33] => fnv132
//    [34] => fnv1a32
//    [35] => fnv164
//    [36] => fnv1a64
//    [37] => joaat
//    [38] => haval128,3
//    [39] => haval160,3
//    [40] => haval192,3
//    [41] => haval224,3
//    [42] => haval256,3
//    [43] => haval128,4
//    [44] => haval160,4
//    [45] => haval192,4
//    [46] => haval224,4
//    [47] => haval256,4
//    [48] => haval128,5
//    [49] => haval160,5
//    [50] => haval192,5
//    [51] => haval224,5
//    [52] => haval256,5
//)

The most common ones I have seen used are md5, sha1, sha256, sha384, sha512, sha3-256, and sha3-384.

There are a couple hash functions you can call by name, but most of them you have to call via the hash function.

$result = md5($some_str); // de48ff410052ff5aefa835baa0730091
$result = sha1($some_str); // 9468ed05787087ef089abd619d064ea02e1a332e
$result = hash("sha256", $some_str); // e89587cefbb65aca58b620b4b73c2414b62d434b4d3922d9d831f0eb1163acf2
$result = hash("sha384", $some_str); // 7f0085e3292ed3fa304f3e0ccc639cf566d7e4adcf716a687fdab9351a9bdbdb27d13ae10d2f6004c8f128bd0feb17e4
$result = hash("sha512", $some_str); // 66b51817cd2d70955b86de0682e73d00c592894010fc0f905687d7e09ece38dbc7de7d4fdeb99b0097453fd20358839b9ec115926ce6128e1d5e3c1be3ec169f
$result = hash("sha3-256", $some_str); // 096abe95b28240d854caa3e6563e6f7eb0252c29fd199f898f3fff145e95445a
$result = hash("sha3-384", $some_str); // e97a5337fee193fc10c86666f6d2fb5308cf0aa8b2127a2bcce1093163aa2bd4593ecf223161e45b240079b4f1a3af4e
$result = hash("sha3-512", $some_str); // 861743cc22ab78c2cc156d0f50202b828b107aa97607962428811ff450b1534202712b88fa394f75826ea73d9b46c14e9f427e54b9fc44a3235ee4e0dd2c13b9

The md5 and sha1 functions are, historically, the most common functions for message-digest hashing. The sha### and sha3-### functions are two groups, which differ mostly by the algorithm and computational complexity required.

For sha### and sha3-### functions return strings of the length ### / 8 * 2, or ### / 4. This is because they work with blocks of a bitsize equal to the number specified, and return the final block in hexadecimal format, which means every 8 bits become 2 characters.


To Conclude

So we went through a long list of functions today, we're going to use them later. The next lesson will be fun, at least I think it will be fun. We'll work with the PHP graphics API to create dynamic images.

Getting Started with PHP: Part 2

Getting started with PHP: Part 2

In our last discussion we got our PHP environment setup, and we talked about some basic parts of PHP:

  • Statements end in semi-colons (;);
  • We use echo to output things to the caller;
  • There are several comparison operators (==, ===, !=, !==, >, <, >=, <=, <=>);
  • We can use if to change what a program does;
  • Variables ($var);

This got our adventure started, so I want to start moving more into PHP as a language. We're going to start building example products, and talking about the features as we do. We will talk about standard functions, special variables and more.

Let's build a form to show the user's name

For this, we're going to build a web-form that we use to ask for a user's name, and then use the logic we used previously to say hello.

First we'll create a PHP file as we did before. The name is only important in-so-far as you will need to remember it for your browser.

Next, we need a form to show ask the user for their name. For this, we're going to talk about PHP strings for a moment.

PHP: Strings

PHP has 2 different types of strings, and two different ways to create each one. The types are:

Delimited double-quoted strings:

$name = "Elliott";

Delimited single-quoted strings:

$name = 'Elliott';

HEREDOC string (double-quoted):

$name = <<<NAME
Elliott
NAME;

NOWDOC string (single-quoted):

$name = <<<'NAME'
Elliott
NAME;

So, at this point you are likely wondering why PHP has 4 different strings. The logic behind why is actually quite sound, so we'll go over it at a rough-level.

First, single-quoted and double-quoted strings behave differently. Likewise, NOWDOC and HEREDOC behave differently, matching their single-quoted or double-quoted counterpart. A double-quoted string allows you to embed variables within it, such as our previous "Hello $my_name". However, a single-quoted string does not allow this. If we changed our string to 'Hello $my_name', we would get Hello $my_name instead of Hello Elliott as our output.

Next, in delimited strings we have to escape the delimiter if we want to embed it within our string. This means if we want to put the delimiter itself in the string (which we often do with HTML/CSS/JavaScript), we need to escape it, such as:

$html = "<form method=\"POST\"></form>";

Notice how the quotes around "POST" are preceeded by a slash (\). This means that the literal double-quote (") will be put there instead of the escape \".

Enter HEREDOC and NOWDOC: because they don't use delimiters you can include the delimiters in the string freely:

$html = <<<HTML
<form method="POST"></form>
HTML;

The rules around HEREDOC and NOWDOC are as follows:

  1. The string begins with three less-than signs (<<<) and then a name, then the line ends;
  2. The string can contain any characters, and most importantly, the delimiters (" / ') do not need to be escaped;
  3. The string ends when a line that begins with the original name and then a semi-colon is encountered (there may be no whitespace at the beginning of the line);
  4. If the opening name is wrapped with single-quotes, it is a 'NOWDOC' (which acts like a single-quoted string), otherwise it is a HEREDOC (which acts like a double-quoted string);

By "acts like a -quoted string", I mean it either replaces variables like a double-quoted string, or it does not like a single-quoted string.

One key note about HEREDOC and NOWDOC is that you cannot use concatenation in them directly, you must create intermediary strings for that:

$html = <<<HTML
<form method="POST">
HTML;

$html = $html . <<<HTML
</form>
HTML;

That said, both HEREDOC and NOWDOC handle line-continuations / CR;LF; properly, so you could simply do:

$html = <<<HTML
<form method="POST">
</form>
HTML;

Back to our Form

Now that we know about strings, we need to start getting our form ready. In PHP, we can do this either by echo-ing the form, or by ending the PHP tags and including the form as raw data.

For our form, we only need two items within it: a text-box for the name, and a button to submit it. Our HTML might look something like the following:

<form method="POST">
    <input type="text" name="name" />
    <input type="submit" name="submit" value="Say Hello" />
</form>

The name on each of these is important as that is how PHP will access the values.

To see this in action, let's create a PHP page with only that in it. No <?php tags or code at all. If you did it properly, you'll see a webpage with a single text-box and button that says "Say Hello".

This demonstrates that we don't need to echo everything, we can just exit the PHP context and write it out, PHP will output it for us.

Obviously we don't always want to show the form, so it's time we talk about how PHP can access the data that an HTML form sends back.

PHP: Posting

When an HTML form posts data back to a server, it sends a request with the Method: POST header, and a body with each name/value pair encoded as name=encodeValue(value), concatenated with ampersands (&). Thus, if we typed Elliott into our text box and clicked "Say Hello", it would post name=Elliott&submit=Say Hello to our server.

PHP has several "superglobals", which are built-in variables that do special things. The two we'll talk about right now are $_GET and $_POST. You can access both of these from any PHP code.

PHP: Arrays

Before we go deeper into superglobals, let's go over arrays very quickly.

In PHP, an array is a key => value mapping. That is: it has any number of unique keys that have exactly one value. Keys can be anything, most commonly they are integers and strings.

There are two ways in PHP 7.4.6 to construct arrays:

$some_array = array (
    "Key 1" => "Value 1",
    "Key 2" => "Value 2"
);
$some_array = [
    "Key 1" => "Value 1",
    "Key 2" => "Value 2"
];

Both of these methods create the same array, and allow us to use $some_array["Key 1"] and $some_array["Key 2"] to access the values in it. We can also use that to set the values, so we could say $some_array["Key 1"] = "New Value";. Additionally, we can add new keys to the array by doing $some_array["Key 3"] = "Value 3";. Additionally, PHP has a built-in function named print_r that will output the contents of an array for us to troubleshoot.

If we change our PHP page to the following:

<?php
$some_array = array (
    "Key 1" => "Value 1",
    "Key 2" => "Value 2"
);
$some_array = [
    "Key 1" => "Value 1",
    "Key 2" => "Value 2"
];
$some_array["Key 1"] = "New Value";
$some_array["Key 3"] = "Value 3";
print_r($some_array);
?>

We will get the following output:

Array ( [Key 1] => New Value [Key 2] => Value 2 [Key 3] => Value 3 )

This shows us the Key 1 did change, and that Key 3 was added.

Additionally, one other thing we need to discuss on arrays is how to determine if a key exists. Specifically, the isset(...) function.

If we have an array, and we don't know if our key is in it, we use isset( $array[$key] ) to test for it. If we try to use $array[$key] directly, we will get an error:

Notice: Undefined index: $key in $file on line $line

This will put ugly output on our page, and while we can suppress it that doesn't help us: the best way to avoid it is test for it.

To do that, we might do the following:

if (isset($_POST["name"])) {
    // do something with our name
}

Back to Superglobals

The $_GET and $_POST superglobals are arrays where the key comes from a component of the request, and the value comes from that value. As an example, our $_POST will have two keys: $_POST["name"] and $_POST["submit"]. These will have the values set to "Elliott" and "Say Hello" respectively.

Additionally, $_GET derives it's keys and values from the URL parameters. If you've used many websites, you've probably noticed at some point you get wonky URL's that have a question-mark (?) and then names, equal-signs (=) and values, maybe even an ampersand (&) between them. This data is encoded the same way as our POST data, but is accessed via $_GET instead of $_POST. If we did index.php?name=Elliott, we could access our name value with $_GET["name"].

Basically:

  • $_GET will give you an array of the URL parameter keys and values;
  • $_POST will give you an array of the POST body keys and values;

With all this in mind, we can actually build our form now.

For our form, we will need an if statement to determine if the form was "submitted" (test if "name" is in the $_POST array), an else to output the form if it wasn't submitted, and in the if we need to test if it's "Jon", because he owes me a lot of money.

One method of doing this boils down to the following:

<?php
if (isset($_POST["name"])) {
    $name = $_POST["name"];
    if ($name === "Jon") {
        echo "You owe me a lot of money, Jon.";
    } else {
        echo "Hello $name";
    }
} else {
?>
<form method="POST">
    <input type="text" name="name" />
    <input type="submit" name="submit" value="Say Hello" />
</form>
<?php
}
?>

Now, you'll notice we ended the PHP processing before the HTML, then started it again afterwards. This is one technique of printing HTML on a page, but it's not my favourite. I prefer to use an echo with a HEREDOC because it seems to read more clearly, and we do less "context switching" between what is PHP code and what is not.

To use the HEREDOC, we change our code to:

<?php
if (isset($_POST["name"])) {
    $name = $_POST["name"];
    if ($name === "Jon") {
        echo "You owe me a lot of money, Jon.";
    } else {
        echo "Hello $name";
    }
} else {
    echo <<<HTML
<form method="POST">
    <input type="text" name="name" />
    <input type="submit" name="submit" value="Say Hello" />
</form>
HTML;
}

To me, this is a cleaner way to output our HTML. This also allows us to embed variables in it if we need to, whereas the other version would require us to do <?php echo $variable; ?>.

More form Elements

I want to go over some more HTML form elements, such as textarea, checkbox, radio and select.

Of these, three play just as well as text did with our PHP: $_POST[$name] will have the value they are. The one that doesn't play as well is checkbox, and the behaviour it has can vary depending on whether you are using Apache or IIS.

With Apache, if isset($_POST[$checkbox_name]) evaluates to TRUE, then the checkbox was selected, regardless of value. With IIS, the checkbox value will be off if it was not submitted, and either on or the specified value if it was submitted.

We'll go over basic elements, and basic attributes that are important on those elements, but please know that this is not an exhaustive list of the elements nor of the attributes.

HTML: Textarea

An HTML textarea is a large text box that supports multiline input. It is included in a form with the following (basic) markup:

<textarea
    [name="$name"]
    [rows="$row_count"]
    [cols="$col_chars"]>
</textarea>

PHP will see $_POST[$name] as the text-value entered.

HTML: Text

An HTML text is a small text box that supports soft-limits on text input. It is included in a form with the following (basic) markup:

<input type="text"
    [name="$name"]
    [value="$default_value"]
    [maxlength="$max_length_chars"] />

PHP will see $_POST[$name] as the text-value entered.

HTML: Radio

An HTML radio is a group of mutually-exclusive options (that is, only one option may be selected). It is included in a form with the following (basic) markup:

<input type="radio"
    [name="$name"]
    [value="$value"]
    [checked="checked"] />

If multiple radio buttons have the same name, they are part of a single "group" and only one of them may be selected. Additionally, you may default which one is selected with the checked="checked" attribute.

PHP will see $_POST[$name] as the $value of the selected radio button.

HTML: Select

An HTML select is a group of mutually-exclusive options (that is, only one option may be selected) that presents as a drop-down list. It is included in a form with the following (basic) markup:

<select [name="$name"]>
    <option [value="$value"] [selected="selected"]>$display_text</option>
</select>

Any number of <option> elements may be included in it. Additionally, you may default which one is selected with the selected="selected" attribute.

PHP will see $_POST[$name] as the $value of the selected drop-down option.

HTML: Checkbox

An HTML checkbox is a single on/off toggle. It is included in a form with the following (basic) markup:

<input type="checkbox"
    [name="$name"]
    [value="$value"]
    [checked="checked"] />

You may default it to the "checked" state with the checked="checked" attribute.

On Apache:

PHP will see $_POST[$name] as $value (or "on" if there is no value) if it is checked, otherwise $_POST[$name] will not be set.

On IIS:

PHP will see $_POST[$name] as $value (or "on" if there is no value) if it is checked, otherwise it will see $_POST[$name] as "off".

Experimenting with Form Elements

To see all of this in action, you can print_r($_POST) and create a basic form with all of these elements:

<?php
print_r($_POST);
echo <<<HTML
<form method="POST">
    <textarea name="textarea">Textarea Text</textarea><br />
    <input type="text" name="text" value="Text Text" /><br />
    <input type="radio" name="radio" value="Option 1" checked="checked" /><br />
    <input type="radio" name="radio" value="Option 2" /><br />
    <input type="checkbox" name="checkbox_on" checked="checked" /><br />
    <input type="checkbox" name="checkbox_on_value" value="Checkbox On" checked="checked" /><br />
    <input type="checkbox" name="checkbox_off" /><br />
    <input type="checkbox" name="checkbox_off_value" value="Checkbox Off" /><br />
    <select name="select">
        <option value="Option 1" selected="selected">Option 1</option>
        <option value="Option 2">Option 2</option>
        <option value="Option 3">Option 3</option>
        <option value="Option 4">Option 4</option>
    </select>
    <input type="submit" name="submit" value="Submit" /><br />
</form>
HTML;

If you submit the default form on your Apache + PHP environment, you will get the following at the beginning of the page:

Array
(
    [textarea] => Textarea Text
    [text] => Text Text
    [radio] => Option 1
    [checkbox_on] => on
    [checkbox_on_value] => Checkbox On
    [select] => Option 1
    [submit] => Submit
)

Cleaning up Form Elements

One of the many things I have my own standard "PHP library" for is cleaning up these form elements, specifically checkboxes.

I typically do this by creating a PHP class with static functions, but we haven't gotten to that point and there's a lot of ground to cover before we get there, so I'm going to start with regular functions to clean this up.

PHP: Functions

PHP has the concept of "functions", or reusable blocks of code that do a certain, specific task. We have used something that looks like a function (isset), but we haven't used a true function yet.

A function is just code, often with inputs and outputs that do a thing. Functions have different "definitions", such as "pure" vs. "impure", where a "pure" function will produce the exact same output for any given input, and an "impure" function will not.

PHP defines functions quite simply: the keyword function followed by the name, a pair of parenthesis (()), then braces ({}) to denote the code in the function. Additionally, in the parenthesis, you may specify parameters to be passed to your function.

One of the relatively new features of PHP is the ability to specify types for the parameters passed to your function, and the type of data the function will return. We're going to use these, as it's a healthy habit to get into.

If we wanted to define a function to get text from our form, we might do the following:

function text(string $name): ?string {
    if (isset($_POST[$name])) {
        return $_POST[$name];
    }
    return NULL;
}

Some of this is new, so let's start from the beginning and hit all the new pieces:

  • function: as discussed above, this tells PHP we're about to define our own function;
  • text: this is the name, and this can be any string that you would name a variable;
  • string: this is the type for the first input parameter, here we say it has to be a string value;
  • $name: this is the name of the first parameter, in our case we're naming it name;
  • : ?string: this specifies the return type of the function, here it says it is a ?string, where the ? means it is a nullable string;
  • return: this tells PHP to end the function execution and give the caller whatever follows the return keyword;
  • NULL: this is a special value that means "there is no value";

The string for the parameter, and : ?string for the return type are optional, and if you do not specify them PHP will default to allowing any input and will not tell the caller what the output is (because it does not know).

We can call this function by specifying the name and then parameters: text("textarea") will be replaced with the value of $_POST["textarea"] per our function at runtime.

My personal PHP library defines a bunch of these functions, but the one I want to mention is the checkbox function, which will take the IIS oddities into account:

function checkbox(string $name): ?string {
    if (isset($_POST[$name]) && $_POST[$name] !== "off") {
        return $_POST[$name];
    }
    return NULL;
}

Here, we notice we have something new: &&. Let's talk about that for a moment.

PHP: Logical Combination Operators

PHP has the concept of combining logical comparison operators together via several chain operators. This allows you to put multiple conditions in your if statements (and other locations, but that's a discussion for later).

  • &&: if the left and right side of this are both TRUE the result is TRUE, otherwise the result is FALSE, additionally if the left side is FALSE the right side is not evaluated;
  • ||: if the left and right side of this are both FALSE the result is FALSE, otherwise the result is TRUE, additionally if the left side is TRUE the right side is not evaluated;
  • !: flips the right side from TRUE to FALSE, or FALSE to TRUE;
  • xor: if the left and right side are not the same boolean the result is TRUE, otherwise the result is FALSE;

As a result, we can use isset($_POST[$name]) && $_POST[$name] !== "off" to say "if $_POST[$name] has a value, and that value is not "off", then continue into our body." Additionally, PHP will not attempt to test $_POST[$name] !== "off" if isset($_POST[$name]) failed.


In Summation

With logical operators covered, I think now is a good time to wrap up. We'll get further into our adventure next time, but we're building a solid foundation on which we'll continue to develop.